什么是 JWT

一个JWT,应该是如下形式的:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

这些东西看上很凌乱,但是非常紧凑,并且是可打印的主要用于验证签名的真实性。

JWT 解决什么问题?

JWT的主要目的是在服务端和客户端之间以安全的方式来转移声明。​​java培训​​主要的应用场景如下所示:

  1. 认证 Authentication;
  2. 授权 Authorization // 注意这两个单词的区别;
  3. 联合识别;
  4. 客户端会话(无状态的会话);
  5. 客户端机密。

JWT 的一些名词解释

  1. JWS:Signed JWT签名过的jwt
  2. JWE:Encrypted JWT部分payload经过加密的jwt;目前加密payload的操作不是很普及;
  3. JWK:JWT的密钥,也就是我们常说的 scret;
  4. JWKset:JWT key set在非对称加密中,需要的是密钥对而非单独的密钥,在后文中会阐释;
  5. JWA:当前JWT所用到的密码学算法;
  6. nonsecure JWT:当头部的签名算法被设定为none的时候,该JWT是不安全的;因为签名的部分空缺,所有人都可以修改。

JWT的组成

一个通常你看到的jwt,由以下三部分组成,它们分别是:

  1. header:主要声明了JWT的签名算法;
  2. payload:主要承载了各种声明并传递明文数据;
  3. signture:拥有该部分的JWT被称为JWS,也就是签了名的JWS;没有该部分的JWT被称为nonsecure JWT 也就是不安全的JWT,此时header中声明的签名算法为none。

三个部分用·分割。形如 xxxxx.yyyyy.zzzzz的样式。

JWT header

{"typ": "JWT","alg": "none","jti": "4f1g23a12aa"
}

jwt header 的组成

头通常由两部分组成:令牌的类型,即JWT,以及正在使用的散列算法,例如HMAC SHA256或RSA。

当然,还有两个可选的部分,一个是jti,也就是JWT ID,代表了正在使用JWT的编号,这个编号在对应服务端应当唯一。当然,jti也可以放在payload中。

另一个是cty,也就是content type。这个比较少见,当payload为任意数据的时候,这个头无需设置,但是当内容也带有jwt的时候。也就是嵌套JWT的时候,这个值必须设定为jwt。这种情况比较少见。

jwt header 的加密算法

加密的方式如下:

base64UrlEncode(header)
>> eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwianRpIjoiNGYxZzIzYTEyYWEifQ

JWT payload

{"iss": "http://shaobaobaoer.cn","aud": "http://shaobaobaoer.cn/webtest/jwt_auth/","jti": "4f1g23a12aa","iat": 1534070547,"nbf": 1534070607,"exp": 1534074147,"uid": 1,"data": {"uname": "shaobao","uEmail": "shaobaobaoer@126.com","uID": "0xA0","uGroup": "guest"}
}

jwt payload的组成payload通常由三个部分组成,分别是 Registered Claims ; Public Claims ; Private Claims ;每个声明,都有各自的字段。

Registered Claims

  • iss 【issuer】发布者的url地址
  • sub 【subject】该JWT所面向的用户,用于处理特定应用,不是常用的字段
  • aud 【audience】接受者的url地址
  • exp 【expiration】 该jwt销毁的时间;unix时间戳
  • nbf 【not before】 该jwt的使用时间不能早于该时间;unix时间戳
  • iat 【issued at】 该jwt的发布时间;unix 时间戳
  • jti 【JWT ID】 该jwt的唯一ID编号

Public Claims 这些可以由使用JWT的那些标准化组织根据需要定义,应当参考文档IANA JSON Web Token Registry。

Private Claims 这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公开声明。上面的payload中,没有public claims只有private claims。

jwt payload 的加密算法

加密的方式如下:

base64UrlEncode(payload)
>>eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19

暴露的信息

所以,在JWT中,不应该在载荷里面加入任何敏感的数据。在上面的例子中,我们传输的是用户的User ID,邮箱等。这个值实际上不是什么敏感内容,一般情况下被知道也是安全的。但是像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了。

当然,这也是有解决方案的,那就是加密payload。在之后会说到

JWS 的概念

JWS 的结构

JWS ,也就是JWT Signature,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。

为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secret。当利用非对称加密方法的时候,这里的secret为私钥。

为了方便后文的展开,我们把JWT的密钥或者密钥对,统一称为JSON Web Key,也就是JWK。

jwt signature 的签名算法

RSASSA || ECDSA || HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
>>GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ
>># 上面这个是用 HMAC SHA256生成的

到目前为止,jwt的签名算法有三种。

  • 对称加密HMAC【哈希消息验证码】:HS256/HS384/HS512
  • 非对称加密RSASSA【RSA签名算法】(RS256/RS384/RS512)
  • ECDSA【椭圆曲线数据签名算法】(ES256/ES384/ES512)

最后将签名与之前的两段内容用.连接,就可以得到经过签名的JWT,也就是JWS。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjRmMWcyM2ExMmFhIn0.eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19.GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ

当验证签名的时候,利用公钥或者密钥来解密Sign,和 base64UrlEncode(header) + "." + base64UrlEncode(payload) 的内容完全一样的时候,表示验证通过。

JWS 的额外头部声明

如果对于CA有些概念的话,这些内容会比较好理解一些。为了确保服务器的密钥对可靠有效,同时也方便第三方CA机构来签署JWT而非本机服务器签署JWT,对于JWS的头部,可以有额外的声明,以下声明是可选的,具体取决于JWS的使用方式。如下所示:

  • jku: 发送JWK的地址;最好用HTTPS来传输
  • jwk: 就是之前说的JWK
  • kid: jwk的ID编号
  • x5u: 指向一组X509公共证书的URL
  • x5c: X509证书链
  • x5t:X509证书的SHA-1指纹
  • x5t#S256: X509证书的SHA-256指纹
  • typ: 在原本未加密的JWT的基础上增加了 JOSE 和 JOSE+ JSON。JOSE序列化后文会说及。适用于JOSE标头的对象与此JWT混合的情况。
  • crit: 字符串数组,包含声明的名称,用作实现定义的扩展,必须由 this->JWT的解析器处理。不常见。

多重验证与JWS序列化

当需要多重签名或者JOSE表头的对象与JWS混合的时候,往往需要用到JWS的序列化。JWS的序列化结构如下所示:

{"payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
"signatures": [{"protected": "eyJhbGciOiJSUzI1NiJ9","header": { "kid": "2010-12-29" },"signature":"signature1"},{"protected": "eyJhbGciOiJSUzI1NiJ9","header": { "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d" },"signature":"signature2"},...]
}

结构很容易理解。首先是payload字段,这个不用多讲,之后是signatures字段,这是一个数组,代表着多个签名。每个签名的结构如下:

  • protected:之前的头部声明,利用b64uri加密;
  • header:JWS的额外声明,这段内容不会放在签名之中,无需验证;
  • signature:也就是对当前header+payload的签名。

JWE 相关概念

JWE是一个很新的概念,总之,除了jwt的官方手册外,很少有网站或者博客会介绍这个东西。也并非所有的库都支持JWE。这里记录一下自己看官方手册后理解下来的东西。

JWS是去验证数据的,而JWE(JSON Web Encryption)是保护数据不被第三方的人看到的。通过JWE,JWT变得更加安全。

JWE和JWS的公钥私钥方案不相同,JWS中,私钥持有者加密令牌,公钥持有者验证令牌。而JWE中,私钥一方应该是唯一可以解密令牌的一方。

在JWE中,公钥持有可以将新的数据放入JWT中,但是JWS中,公钥持有者只能验证数据,不能引入新的数据。因此,对于公钥/私钥的方案而言,JWS和JWE是互补的。

JWE 的构成

一个JWE,应该是如下形式的:

eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.
UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_
i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKxYxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8Otv
zlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTPcFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.
AxY8DCtDaGlsbGljb3RoZQ.
KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.
9hH0vgRfYgPnAHOd8stkvw

如你所见JWE一共有五个部分,分别是:

  • The protected header,类似于JWS的头部;
  • The encrypted key,用于加密密文和其他加密数据的对称密钥;
  • The initialization vector,初始IV值,有些加密方式需要额外的或者随机的数据;
  • The encrypted data (cipher text),密文数据;
  • The authentication tag,由算法产生的附加数据,来防止密文被篡改。

JWE 密钥加密算法

一般来说,JWE需要对密钥进行加密,这就意味着同一个JWT中至少有两种加密算法在起作用。但是并非将密钥拿来就能用,我们需要对密钥进行加密后,利用JWK密钥管理模式来导出这些密钥。JWK的管理模式有以下五种,分别是:

  • Key Encryption
  • Key Wrapping
  • Direct Key Agreement
  • Key Agreement with Key Wrapping
  • Direct Encryption

并不是所有的JWA都能够支持这五种密钥管理管理模式,也并非每种密钥管理模式之间都可以相互转换。至于各个密钥管理模式的细节,还请看JWT的官方手册,解释起来较为复杂。

JWE Header

就好像是JWS的头部一样。JWE的头部也有着自己规定的额外声明字段,如下所示:

  • type:一般是 jwt
  • alg:算法名称,和JWS相同,该算法用于加密稍后用于加密内容的实际密钥
  • enc:算法名称,用上一步生成的密钥加密内容的算法。
  • zip:加密前压缩数据的算法。该参数可选,如果不存在则不执行压缩,通常的值为 DEF,也就是deflate算法
  • jku/jkw/kid/x5u/x5c/x5t/x5t#S256/typ/cty/crit:和JWS额额外声明一样。

JWE 的加密过程

步骤2和步骤3,更具不同的密钥管理模式,应该有不同的处理方式。在此只罗列一些通常情况。

之前谈及,JWE一共有五个部分。现在来详细说一下加密的过程:

  1. 根据头部alg的声明,生成一定大小的随机数;
  2. 根据密钥管理模式确定加密密钥;
  3. 根据密钥管理模式确定JWE加密密钥,得到CEK;
  4. 计算初始IV,如果不需要,跳过此步骤;
  5. 如果ZIP头申明了,则压缩明文;
  6. 使用CEK,IV和附加认证数据,通过enc头声明的算法来加密内容,结果为加密数据和认证标记;
  7. 压缩内容,返回token。
base64(header) + '.' +base64(encryptedKey) + '.' + // Steps 2 and 3base64(initializationVector) + '.' + // Step 4base64(ciphertext) + '.' + // Step 6base64(authenticationTag) // Step 6

多重验证与JWE序列化

和JWS类似,JWE也定义了紧凑的序列化格式,用来完成多种形式的加密。大致格式如下所示:

{"protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","unprotected": { "jku":"https://server.example.com/keys.jwks" },"recipients":[{"header": { "alg":"RSA1_5","kid":"2011-04-29" },"encrypted_key":"UGhIOguC7Iu...cqXMR4gp_A"},{"header": { "alg":"A128KW","kid":"7" },"encrypted_key": "6KB707dM9YTIgH...9locizkDTHzBC2IlrT1oOQ"}],"iv": "AxY8DCtDaGlsbGljb3RoZQ","ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY","tag": "Mz-VPPyU4RlcuYv1IwIvzw"
}

结构很容易理解,如下所示:

  • protected:之前的头部声明,利用b64uri加密;
  • unprotected:一般放JWS的额外声明,这段内容不会被b64加密;
  • iv:64加密后的iv参数;
  • add:额外认证数据;
  • ciphertext:b64加密后的加密数据;
  • recipients:b64加密后的认证标志-加密链,这是一个数组,每个数组中包含了两个信息;
  • header:主要是声明当前密钥的算法;
  • encrypted_key:JWE加密密钥。

JWT 的工作原理

这里通过juice shop来说下jwt是如何工作的。在身份验证中,当用户使用其凭据成功登录时,将返回JSON Web令牌。如下所示:往此时,返回了jwt的令牌。

每当用户想要访问受保护的路由或资源时,用户将使用承载【bearer】模式发送JWT,通常在Authorization标头中。标题的内容应如下所示:

Authorization: Bearer <token>

随后,服务器会取出token中的内容,来返回对应的内容。须知,这个token不一定会储存在cookie中,如果存在cookie中的话,需要设置为http-only,防止XSS。另外,还可以放在别的地方,比如localStorage、sessionStorage。如果使用vue的话,还可以存在vuex里面。

另外,如果在如Authorization: Bearer中发送令牌,则跨域资源共享(CORS)将不会成为问题,因为它不使用cookie。

此时,去访问认证页面,请求头如下所示,如预期所见,是利用Authorization:Bearer的请求头去访问的。

ECDSA|RSASSA or HMAC ?应该选用哪个?

首先,我们必须明确一点,无论用的是 HMAC,RSASSA,ECDSA;密钥,公钥,私钥都不会发送给客户端,仅仅会保留在服务端上。

对称的算法HMAC适用于单点登录,一对一的场景中。速度很快。

但是面对一对多的情况,比如一个APP中的不同服务模块,需要JWT登录的时候,主服务端【APP】拥有一个私钥来完成签名即可,而用户带着JWT在访问不同服务模块【副服务端】的时候,副服务端只要用公钥来验证签名就可以了。从一定程度上也减少了主服务端的压力。

当然,还有一种情况就是不同成员进行开发的时候,大家可以用统一的私钥来完成签名,然后用各自的公钥去完成对JWT的认证,也是一种非常好的开发手段。

因此,构建一个没有多个小型“微服务应用程序”的应用程序,并且开发人员只有一组的,选择HMAC来签名即可。其他情况下,尽量选择RSA。

作者:NinthDevilHunster

java开发中JWT、JWE、JWS 、JWK 都是干啥的相关推荐

  1. Java开发中经常使用到的几种WebService技术实现方案

    Java开发中经常使用到的几种WebService技术实现方案 随着异构系统互联需求的不断增加,WebService的重要性也日益彰显出来.凭借webservice,我们可以实现基于不同程序语言的项目 ...

  2. Java开发中Websocket的技术选型参考

    1. 前言 Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据 ...

  3. Java开发中文件读取方式总结

    JAVA开发中,免不了要读文件操作,读取文件,首先就需要获取文件的路径.路径分为绝对路径和相对路径. 在文件系统中,绝对路径都是以盘符开始的,例如C:abc1.txt. 什么是相对路径呢?相对路径就是 ...

  4. Java开发中Netty线程模型原理解析!

    Java开发中Netty线程模型原理解析,Netty是Java领域有名的开源网络库具有高性能和高扩展性的特点,很多流行的框架都是基于它来构建.Netty 线程模型不是一成不变的,取决于用户的启动参数配 ...

  5. Java开发中更多常见的危险信号

    在< Java开发中的常见危险信号>一文中,我研究了一些不一定本身就是错误或不正确的做法,但它们可能表明存在更大的问题. 这些"红色标记"类似于"代码气味&q ...

  6. Java开发中的常见危险信号

    在开发,阅读,复审和维护成千上万行Java代码的几年中,我已经习惯于看到Java代码中的某些" 危险信号 ",这些信号通常(但可能并非总是)暗示着代码问题. 我不是在谈论总是错误的 ...

  7. java property xml,Java开发中读取XML与properties配置文件的方法

    相关阅读: 1. XML文件: 什么是XML?XML一般是指可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. 2.XML文件的优点: 1)XML文档内容和结构完 ...

  8. java 线程 john_教你Java开发中多线程同步技巧

    教你Java开发中多线程同步技巧 在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简化了Java ...

  9. java路径Java开发中获得非Web项目的当前项目路径

    最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--java路径 Java开发中指定路径搜索文件也是经常遇到的,比如遍历某个目录下的文件.须要取得项目的路径.调用调查主要有两种 1.利用S ...

最新文章

  1. Windows核心编程(3)字符编码详解
  2. 【OpenGL ES】 Android OpenGL ES -- 透视投影 和 正交投影
  3. 洛谷 P3455BZOJ1101 【[POI2007]ZAP-Queries】
  4. 通讯业2月网络广告投放环比增33% 联通居首位
  5. Redis实战(六):Redis的集群:AKF,主从复制的概念引入,CAP,Sentinel 哨兵
  6. karaf内嵌文件服务器,关于OSGI(Karaf) Classloader的几点说明
  7. thinkphp手机版小说网站源码
  8. 结构设计模式 - 代理设计模式
  9. 北航 计算机学院 2011级学生会,北航学生会主席在2011级新生开学典礼发言稿.doc...
  10. string中c_str()用法总结
  11. BigDecimal的加减乘除
  12. SpringBoot配置多Redis数据源
  13. SpringBoot+Querydsl 框架,大大简化复杂查询操作
  14. html弹出对话框 自动点击,c#如何自动点击网页弹出的对话框的确认键
  15. react 打包体积过大_create-react-app andt 打包的 js 文件过大
  16. 移动端跨平台开发的深度解析
  17. 【STM32项目】老人健康跌倒检测系统实现
  18. 在英特尔独立显卡上训练TensorFlow模型
  19. 511遇见安卓手机投屏支持一键转无线群控
  20. 忘记Hololens开机密码,重置系统

热门文章

  1. oracle聚簇因子对于索引使用的影响
  2. python 使用smtp发送群邮件
  3. 200行JS代码为你的网页挂上红灯笼
  4. swagger2 使用教程
  5. 鸿蒙不用百度网盘,百度网盘限速有救了!官方新出2种方法,不用开会员
  6. 统计学(1)二项分布 几何分布 泊松分布 指数分布 正态分布 gamma分布 beta分布
  7. 离散选择模型之Gumbel分布
  8. ios 图片缩放,等比例压缩
  9. 家用路由器电源适配器9v和12v可以通用吗?
  10. 华为折叠x2是鸿蒙系统吗,华为mateX2发布,鸿蒙系统四月上线,matex2首批搭载。...