简介

什么是JWT?

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

JWT有什么用?

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

JWT的组成

JWT由3部分组成,用拼接

  1. eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6IjZiOTJkNiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjUwMjEyMDcsImp0aSI6IjNiYzcxYTM2LWUxNjItNDEwYS04YTQyLWU1YzY4YTczZTRjZSJ9.UI_Cv0GEW54e9uaz39Ag0V3HreDwxLSlaSfuVuGhZIM

三部分分别是:

Header:头部

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

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. }

JwtUtil

  1. public class JwtUtil {
  2. private static long time = 1000 * 5;
  3. private static String signature = "admin";
  4. public static String createToken() {
  5. JwtBuilder builder = Jwts.builder();
  6. String jwtToken = builder
  7. // header
  8. .setHeaderParam("typ","JWT")
  9. .setHeaderParam("alg","HS256")
  10. // payload
  11. .claim("username","admin")
  12. .claim("role","admin")
  13. // 设置主题
  14. .setSubject("admin-test")
  15. // 设置有效时间
  16. .setExpiration(new Date(System.currentTimeMillis()+time))
  17. .setId(UUID.randomUUID().toString())
  18. // signature
  19. .signWith(SignatureAlgorithm.HS256,signature)
  20. // 拼接三部分
  21. .compact();
  22. return jwtToken;
  23. }
  24. public static boolean checkToken(String token) {
  25. if (token == null) {
  26. return false;
  27. }
  28. try {
  29. // 只判断token是否有效
  30. Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
  31. } catch (Exception e) {
  32. return false;
  33. }
  34. return true;
  35. }
  36. }