JWT
Json Web Token(JWT) 是个轻量级规范,允许用户和服务器之间传递安全可靠信息;
JWT构成
JWT 实际是个字符串,由 header(头部), payload(载荷), signature(签名)组成;
header(头部)
头部描述 JWT 最基本信息,例如类型和签名用的算法,比如
{"typ":"JWT","alg":"HS256"}
以上头部 签名是HS256算法, base64加密
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload(载荷)
(1)标准中注册的声明(建议但不强制使用)
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
(2)公共声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
(3)私有声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
这个指的就是自定义的claim。比如下面面结构举例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:
JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证(还不知道是否能够验证);
而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行。
定义一个 payload
{"sub":"1234567890","name":"John Doe","admin":true}
对其 base64 加密得到 jwt 第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature)
jwt 第三部分是一个签证信息,用于校验数据是否被篡改;
由header(base64后的), payload(base64之后的), secret(盐) 三部分组成:
这个部分需要base64加密后的header和base64加密后的payload使用 . 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后构成 jwt 第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
签名总结:Base64(头) + Base64(载荷) + 密钥(盐) ---》加密 采用 头中指定的算法
签名作用: 用于校验令牌 是否篡改
令牌:
加密测试
@Test public void testCreateToken(){ JwtBuilder builder = Jwts.builder(); builder.setIssuer("测试人"); builder.setIssuedAt(new Date()); builder.setSubject("令牌Test"); builder.signWith(SignatureAlgorithm.HS256,"itCheck"); System.out.println(builder.compact()); }
输出
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiLmtYvor5XkuroiLCJpYXQiOjE1OTc3MzM4MjUsInN1YiI6IuS7pOeJjFRlc3QifQ.Mjq7bFrsJELQBr0j5wYm4LoY3QiNerUMVJ5wDQKN590
解密测试
@Test public void testCreateToken(){ JwtBuilder builder = Jwts.builder(); //构建令牌对象 builder.setIssuer("测试人"); builder.setIssuedAt(new Date()); builder.setSubject("令牌Test"); //添加载荷 Map<String, Object> userInfo = new HashMap<String,Object>(); userInfo.put("company","Baidu"); userInfo.put("address","Shenzhen"); builder.addClaims(userInfo); //添加载荷 builder.signWith(SignatureAlgorithm.HS256,"itCheck"); System.out.println(builder.compact()); }
输出
{iss=测试人, iat=1597733825, sub=令牌Test}