简介

什么是JWT?

JSON Web Token,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息

JWT有什么用?

JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求的之前,都要先进行JWT安全校验,通过之后再进行处理。

JWT的组成

JWT由3部分组成,用拼接

  1. eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM

三部分分别是:

Header:

  • jwt的头部由两部分信息组成:
    • type:声明类型,这里是jwt
    • alg:声明加密的算法 通常直接使用 HMAC SHA256
  • 完整的头部信息如下:
    1. {
    2. 'typ': 'JWT',
    3. 'alg': 'HS256'
    4. }

    对头部信息进行Base64编码的得到第一部分的信息

Payload:

载荷就是存放有效信息的地方,它包含声明(要求)。声明有三种类型:

  • registered claims:标准中注册的声明。这里有一组预定义的声明,它们不是强制的,但是推荐
  • public claims:公共的声明
  • private claims:私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

公共的声明 :公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 :私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

一个json字符创,包含一些自定义的信息,

  1. {
  2. "sub": "1234567890",
  3. "name":"John Doe",
  4. "admin":true
  5. }

使用base64加密

  1. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

Signature

由头部信息使用base64加密之后,拼接上载荷使用base64加密之后的部分,在加上当前的密钥,进行头部中的加密算法进行加密

  1. header (base64后的)
  2. payload (base64后的)
  3. secret

第三部分需要base64加密后的header和base64加密后的payload使用 . 连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了JWT的第三部分。

  1. TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  1. var encodedstring = base64urlEncode(header)+'.'+base64UrlEncode(payload);
  2. var signature = HMACSHA256 (encodedstring,'secret');

注意:
secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行JWT的签发和JWT的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发JWT了。

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

  1. eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM

测试

  1. private long time = 1000 * 60 * 60 * 24;
  2. private String signature = "admin";
  3. /**
  4. * 测试JWT
  5. */
  6. @org.junit.Test
  7. public void jwt() {
  8. JwtBuilder builder = Jwts.builder();
  9. String jwtToken = builder
  10. // header
  11. .setHeaderParam("typ","JWT")
  12. .setHeaderParam("alg","HS256")
  13. // payload
  14. .claim("username","6b92d6")
  15. .claim("role","admin")
  16. // 设置主题
  17. .setSubject("admin-test")
  18. // 设置有效时间
  19. .setExpiration(new Date(System.currentTimeMillis()+time))
  20. .setId(UUID.randomUUID().toString())
  21. // signature
  22. .signWith(SignatureAlgorithm.HS256,signature)
  23. // 拼接三部分
  24. .compact();
  25. System.out.println(jwtToken);
  26. }
  27. /**
  28. * 解析
  29. */
  30. @org.junit.Test
  31. public void parse() {
  32. String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM";
  33. JwtParser parser = Jwts.parser();
  34. Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
  35. Claims claims = claimsJws.getBody();
  36. System.out.println(claims.get("username"));
  37. System.out.println(claims.get("role"));
  38. System.out.println(claims.getId());
  39. System.out.println(claims.getExpiration());
  40. }
  41. //
  42. /**
  43. * 创建token
  44. */
  45. @Test
  46. public void testCreateToken() {
  47. //创建 JwtBuilder 对象
  48. JwtBuilder jwtBuilder = Jwts.builder()
  49. //声明的标识,{"jti":"8888"}
  50. .setId("8888")
  51. //设置主体,用户{"sub":"Rose"}
  52. .setSubject("Rose")
  53. //创建时间{"ita":"xxxx"}
  54. .setIssuedAt(new Date())
  55. //创建签证,参数1:算法,参数2:盐
  56. .signWith(SignatureAlgorithm.HS256,"xxxx");
  57. //获取jwt生成的token
  58. String token = jwtBuilder.compact();
  59. System.out.println(token);
  60. System.out.println("============");
  61. String[] split = token.split("\\.");
  62. System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
  63. System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
  64. //无法解密签名
  65. System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
  66. }
  67. /**
  68. * 解析token
  69. */
  70. @Test
  71. public void testParseToken() {
  72. String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwNjc3Nn0.pPDKl5_f5f9RHAfGqzaAiwGwQdpOUYMjiQ9BCzWlUR0";
  73. //解析token得到负载中声明的对象
  74. Claims claims = Jwts.parser()
  75. //密钥
  76. .setSigningKey("xxxx")
  77. .parseClaimsJws(token)
  78. //得到主体
  79. .getBody();
  80. System.out.println("id:" + claims.getId());
  81. System.out.println("Subject:" + claims.getSubject());
  82. System.out.println("IssuedAt:" + claims.getIssuedAt());
  83. }
  84. /**
  85. * 创建token(失效时间)
  86. */
  87. @Test
  88. public void testCreateTokenHasExp() {
  89. //当前时间
  90. long now = System.currentTimeMillis();
  91. //过期时间,一分钟
  92. long exp = now + 60 * 1000;
  93. //创建 JwtBuilder 对象
  94. JwtBuilder jwtBuilder = Jwts.builder()
  95. //声明的标识,{"jti":"8888"}
  96. .setId("8888")
  97. //设置主体,用户{"sub":"Rose"}
  98. .setSubject("Rose")
  99. //创建时间{"ita":"xxxx"}
  100. .setIssuedAt(new Date())
  101. //创建签证
  102. .signWith(SignatureAlgorithm.HS256,"xxxx")
  103. //设置过期时间
  104. .setExpiration(new Date(exp));
  105. //获取jwt生成的token
  106. String token = jwtBuilder.compact();
  107. System.out.println(token);
  108. System.out.println("============");
  109. String[] split = token.split("\\.");
  110. System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
  111. System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
  112. //无法解密签名
  113. System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
  114. }
  115. /**
  116. * 解析token(失效时间)
  117. */
  118. @Test
  119. public void testParseTokenHasExp() {
  120. String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwNzgyOSwiZXhwIjoxNjI2NjA3ODg5fQ.6URP2EbehTS2qx_t0medQUPQxaxBWnUl0VJxuEdlboQ";
  121. //解析token得到负载中声明的对象
  122. Claims claims = Jwts.parser()
  123. //密钥
  124. .setSigningKey("xxxx")
  125. .parseClaimsJws(token)
  126. //得到主体
  127. .getBody();
  128. System.out.println("id:" + claims.getId());
  129. System.out.println("Subject:" + claims.getSubject());
  130. System.out.println("IssuedAt:" + claims.getIssuedAt());
  131. //格式化时间
  132. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  133. System.out.println("签发时间" + simpleDateFormat.format(claims.getIssuedAt()));
  134. System.out.println("过期时间" + simpleDateFormat.format(claims.getExpiration()));
  135. System.out.println("当前时间" + simpleDateFormat.format(new Date()));
  136. }
  137. /**
  138. * 创建token(自定义申明)
  139. */
  140. @Test
  141. public void testCreateTokenByClaims() {
  142. //创建 JwtBuilder 对象
  143. JwtBuilder jwtBuilder = Jwts.builder()
  144. //声明的标识,{"jti":"8888"}
  145. .setId("8888")
  146. //设置主体,用户{"sub":"Rose"}
  147. .setSubject("Rose")
  148. //创建时间{"ita":"xxxx"}
  149. .setIssuedAt(new Date())
  150. //创建签证,参数1:算法,参数2:盐
  151. .signWith(SignatureAlgorithm.HS256,"xxxx")
  152. //自定义申明
  153. .claim("roles","admin")
  154. .claim("logo","xxx.jpg");
  155. //直接传入map
  156. //.addClaims(map);
  157. //获取jwt生成的token
  158. String token = jwtBuilder.compact();
  159. System.out.println(token);
  160. System.out.println("============");
  161. String[] split = token.split("\\.");
  162. System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
  163. System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
  164. //无法解密签名
  165. System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
  166. }
  167. /**
  168. * 解析token(自定义申明)
  169. */
  170. @Test
  171. public void testParseTokenByClaims() {
  172. String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTYyNjYwODc3MCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJ4eHguanBnIn0.qZUoGPlEELhhObwMmp9pQ3ojrc_xnNTlkTNiJLXwWrc";
  173. //解析token得到负载中声明的对象
  174. Claims claims = Jwts.parser()
  175. //密钥
  176. .setSigningKey("xxxx")
  177. .parseClaimsJws(token)
  178. //得到主体
  179. .getBody();
  180. System.out.println("id:" + claims.getId());
  181. System.out.println("Subject:" + claims.getSubject());
  182. System.out.println("IssuedAt:" + claims.getIssuedAt());
  183. System.out.println("roles:" + claims.get("roles"));
  184. System.out.println("logo:" + claims.get("logo"));
  185. }