JWT是什么:

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。
Ø 官方网址:https://jwt.io/
Ø 调试页面:https://jwt.io/
Ø 学习文档:https://jwt.io/introduction/

用途:

Ø 授权:这是我们使用JWT最广泛的应用场景。一次用户登录,后续请求将会包含JWT,对于那些合法的token,允许用户连接路由,服务和资源。目前JWT广泛应用在SSO(Single Sign On)(单点登录)上。因为他们开销很小并且可以在不同领域轻松使用。
Ø 信息交换:JSON Web Token是一种在各方面之间安全信息传输的好的方式 因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。 此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。

组成:

一个JWT由三部分组成,各部分以点分隔:

Header(头部)——-base64编码的Json字符串
Payload(载荷)—-base64编码的Json字符串
Signature(签名)—-使用指定算法,通过Header和Playload加盐计算的字符串

一个JWT看起来像下面这样:

xxxxx.yyyyy.zzzzz

image.png

header

此部分有两部分组成:
Ø 一部分是token的类型,目前只能是JWT
Ø 另一部分是签名算法,比如HMAC 、 SHA256 、 RSA
示例:

{
“alg”:”HS256”,
“typ”:”JWT”
}

base64编码命令:

echo -n ‘{“alg”:”HS256”,”typ”:”JWT”}’ | base64
或者
echo -n “{\“alg\“:\“HS256\“,\“typ\“:\“JWT\“}” | base64

Payload

token的第二部分是payload(有效负载),其中包含claims(声明)。Claims是关于一个实体(通常是用户)和其他数据类型的声明。
claims有三种类型:registered,public,and private claims。

Ø Registered(已注册的声明):这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(观众)and others。(请注意,声明名称只有三个字符,因为JWT意味着紧凑。)
Ø Public(公开声明):这些可以由使用JWT的人随意定义。 但为避免冲突,应在IANA JSON Web Token Registry中定义它们,或者将其定义为包含防冲突命名空间的URI。
Ø private (私人声明):这些声明是为了在同意使用它们的各方之间共享信息而创建的,并且既不是注册声明也不是公开声明。

Signature

要创建签名部分,您需要使用base64编码后的header,base64编码后的payload,a secret,标头中指定的算法,并对其进行签名。
示例:

HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)

Base64和Base64Url 的区别

Base64Url在Base64的基础上进行以下处理:

  1. 去除了尾部填充的“=”
  2. 把“+”替换成“-”
  3. 斜线“/”替换成下划线“_”

Spring-boot集成

导入依赖:

  1. <dependency>
  2. <groupId>com.auth0</groupId>
  3. <artifactId>java-jwt</artifactId>
  4. <version>4.0.0</version>
  5. </dependency>

编写JwtUtil:

  1. @Component
  2. @Slf4j
  3. public class JwtUtil {
  4. /**
  5. * 创建token
  6. * @return
  7. */
  8. public String createToken() {
  9. // token 密钥
  10. String secret = "123456";
  11. //签名算法
  12. Algorithm algorithm = Algorithm.HMAC256(secret);
  13. // 头部信息
  14. Map<String, Object> map = new HashMap<>();
  15. //JWT 的签名算法
  16. map.put("alg", "HS256");
  17. //token的类型
  18. map.put("typ", "JWT");
  19. //获取当前时间
  20. long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;
  21. //获取当前日期
  22. Date nowDate = new Date(currentTime);
  23. //当前时间加上30分钟
  24. Date expireDate = new Date(currentTime + 30 * 60 * 1000);// 半小时过期
  25. //创建token
  26. String token = JWT.create()
  27. .withHeader(map)// 设置头部信息 Header
  28. .withIssuer("Thomas")//设置 载荷 签名是有谁生成 例如 服务器
  29. .withSubject("this is test token")//设置 载荷 签名的主题
  30. // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
  31. .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
  32. .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
  33. .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
  34. .sign(algorithm);//签名 Signature
  35. log.info("生成的JWT 为:"+token);
  36. return token;
  37. }
  38. /**
  39. * 校验token
  40. * @param token
  41. * @throws UnsupportedEncodingException
  42. */
  43. public void verifyToken(String token) throws UnsupportedEncodingException {
  44. // token 密钥
  45. String secret = "123456";
  46. Algorithm algorithm = Algorithm.HMAC256(secret);
  47. JWTVerifier verifier = JWT.require(algorithm).withIssuer("Thomas").build(); // Reusable verifier instance
  48. try {
  49. DecodedJWT jwt = verifier.verify(token);
  50. String subject = jwt.getSubject();
  51. List<String> audience = jwt.getAudience();
  52. Map<String, Claim> claims = jwt.getClaims();
  53. for (Map.Entry<String, Claim> entry : claims.entrySet()) {
  54. String key = entry.getKey();
  55. Claim claim = entry.getValue();
  56. System.out.println("key:" + key + " value:" + claim.asString());
  57. }
  58. // Claim claim = claims.get("loginName");
  59. //
  60. // System.out.println(claim.asString());
  61. // System.out.println(subject);
  62. // System.out.println(audience.get(0));
  63. }catch(Exception ex){
  64. System.out.println("验证失败!!");
  65. }
  66. }
  67. public String createTokenWithChineseClaim() {
  68. long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;
  69. Date nowDate = new Date(currentTime);
  70. Date expireDate = new Date(currentTime+ 30 * 60 * 1000);// 半小时过期
  71. Map<String, Object> map = new HashMap<String, Object>();
  72. map.put("alg", "HS256");
  73. map.put("typ", "JWT");
  74. Algorithm algorithm = Algorithm.HMAC256("secret");
  75. String token = JWT.create().withHeader(map)
  76. /* 设置 载荷 Payload */
  77. .withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "张三").withClaim("deptName", "技术部")
  78. .withIssuer("SERVICE")// 签名是有谁生成 例如 服务器
  79. .withSubject("this is test token")// 签名的主题
  80. // .withNotBefore(new Date())//定义在什么时间之前,该jwt都是不可用的
  81. .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
  82. .withIssuedAt(nowDate) // 生成签名的时间
  83. .withExpiresAt(expireDate)// 签名过期的时间
  84. /* 签名 Signature */
  85. .sign(algorithm);
  86. return token;
  87. }
  88. // public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {
  89. //
  90. // long currentTime = System.currentTimeMillis();// + 30 * 60 * 1000;
  91. //
  92. // Date nowDate = new Date(currentTime);
  93. //
  94. //
  95. // Date expireDate = new Date(currentTime+ 30 * 60 * 1000);// 半小时过期
  96. // //生产头部信息
  97. // Map<String, Object> map = new HashMap<String, Object>();
  98. // map.put("alg", "HS256");
  99. // map.put("typ", "JWT");
  100. //
  101. // Member member = new Member();
  102. // member.setMemberAccount("admin");
  103. // member.setMemberName("张三");
  104. //
  105. // Gson gson = new Gson();
  106. //
  107. //
  108. // String userJson = gson.toJson(member);
  109. //
  110. //
  111. // String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());
  112. //
  113. // Algorithm algorithm = Algorithm.HMAC256("secret");
  114. // String token = JWT.create().withHeader(map)
  115. //
  116. // .withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 签名是有谁生成
  117. // .withSubject("this is test token")// 签名的主题
  118. // // .withNotBefore(new Date())//该jwt都是不可用的时间
  119. // .withAudience("APP")// 签名的观众 也可以理解谁接受签名的
  120. // .withIssuedAt(nowDate) // 生成签名的时间
  121. // .withExpiresAt(expireDate)// 签名过期的时间
  122. // .sign(algorithm);//签名 Signature
  123. //
  124. // return token;
  125. //
  126. // }
  127. }

编写测试类:

  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class JwtUtilTest {
  4. @Resource
  5. private JwtUtil jwtUtil;
  6. @Test
  7. public void createToken() {
  8. jwtUtil.createToken();
  9. }
  10. @Test
  11. public void verifyToken() {
  12. String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0aGlzIGlzIHRlc3QgdG9rZW4iLCJhdWQiOiJBUFAiLCJpc3MiOiJUaG9tYXMiLCJleHAiOjE2MTkwNzM5NTAsImlhdCI6MTYxOTA3MjE1MH0.7Os9SyBdsFbEgMZoG6ZwOlqqgx0qpbk36B2AOKKixXY";
  13. try {
  14. jwtUtil.verifyToken(token);
  15. } catch (UnsupportedEncodingException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }