-
JWT(Json Wen Token)原理剖析
JWT(即json web token),大家先看下面这张图
大家可以观察到,jwt String就是生成后的jwt字符集,其中有两个 "."(注意:jwt校验会对"."个数校验,多或少都会校验失败),被"."分割的就是jwt的三个构成部分,即:header、payload、sign。
接下来,给大家讲下jwt的生成规则和校验规则
JWT生成规则:
1、设置加密方式、claims信息(即payload)和signingkey
2、设置加密方式为header,并进行base编码
3、设置claims信息为payload,并进行base64编码
4、对header和payload用"."拼接成jwt,,使用signingkey按照加密方式进行加密,生成sign
5、对Jwt和sign用"."生成最终的jwt
JWT校验规则:
1、设置jwt和signingkey
2、按"."对jwt分成三部分,即:header、payload、sign
3、取第一部分进行base64解码,获取加密方式
4、取第二部分进行base64解码,获取业务参数,即payload
5、使用加密方式和signingkey创建校验器,对header+payload进行加密,并与sign(即第三部分)进行对比
6、取出payload的有效期进行校验,是否过期
7、通过校验,返回claims信息
附:
1、进行base64编码时,会判断是否为android客户端,使用base64的库不一样,这点可以自行看源码。
2、jwt会对生成的base64字符集的特殊符号进行转换,"-"换为"+",“_”换位"/",去掉尾部"="
3、jwt校验时,会判断是否有且只有两个".",否则校验失败
原理总结:
1、三部分里的header和payload是独立的,无须signingkey,只需base64解码即可看到payload的信息,所以千万不要在payload里放敏感信息
2、如果是使用jwt作为登录态校验,建议使用对称加密,因为非对称解密效率相对较慢,较多请求下会影响性能
3、因jwt是无状态的,之前见很多同学使用redis进行存储,我不是很明白,这岂不是违反了jwt当初的设计原则
4、保证密钥不要泄露,否则jwt可以被伪造
5、必要情况下,建议使用https
另附上个人的代码供大家参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
package com.yhc.demo.plugin; import java.io.UnsupportedEncodingException; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; /** * JwtToken工具类 */ @Configuration public class JwtService { private static final Logger log = LoggerFactory.getLogger(JwtService. class ); @Value ( "${jwt.secret:123456}" ) private String secret; @Value ( "${jwt.expiration:60}" ) private Long expiration; /** * 生成token * * @param username * @return token */ public String generateToken(String username) { Claims claims = Jwts.claims(); claims.setIssuer(username); // jwt发行人 claims.setIssuedAt( new Date()); // jwt生成时间 claims.setExpiration(getExp()); // jwt过期时间 claims.setSubject( "auth" ); // jwt主题 claims.setAudience( "yhc" ); // jwt接受方 claims.setId( "uuid" ); // jwt唯一身份标识 claims.setNotBefore( new Date()); // jwt在此之前不可用 return generateToken(claims); } /** * 刷新token * * @param old token * @return new token */ public String refreshToken(String token) { Claims claims = validToken(token); if (claims == null ) { return null ; } claims.setIssuedAt( new Date()); claims.setExpiration(getExp()); return generateToken(claims); } /** * 根据token获取发行人 * * @param token * @return issuer */ public String getIssuer(String token) { Claims claims = validToken(token); return claims.getIssuer(); } /** * 校验jwtToken,如无效,则返回Null,反之,返回负载对象 */ private Claims validToken(String token) { Claims claims = getClaimsFromToken(token); if (claims != null ) { Date exp = claims.getExpiration(); if (exp.before( new Date())) { log.warn( "# jwtToken已失效:{}" , token); throw new RuntimeException( "invalid token" ); } } return claims; } /** * 从token中获取JWT中的负载参数 */ private Claims getClaimsFromToken(String token) { Claims claims = null ; try { claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } catch (Exception e) { log.warn( "# jwtToken格式验证失败:{}" , token, e); throw new RuntimeException( "token verification failed" ); } return claims; } /** 根据负载参数生成token */ private String generateToken(Claims claims) { return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); } /** 获取token有效期 */ private Date getExp() { return new Date(System.currentTimeMillis() + expiration * 1000 ); } public static void main(String[] args) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException, UnsupportedEncodingException { String username = "yhc" ; Claims claims = Jwts.claims(); claims.setIssuer(username); // jwt发行人 claims.setIssuedAt( new Date()); // jwt生成时间 claims.setExpiration( new Date(System.currentTimeMillis() + 3600 * 1000 )); // jwt过期时间 claims.setSubject( "test" ); // jwt主题 claims.setAudience( "yhc" ); // jwt接受方 claims.setId( "uuid" ); // jwt唯一身份标识 // claims.setNotBefore(new Date()); // jwt在此之前不可用 String visitTK = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, "sad12f" ).compact(); System.out.println(visitTK); // SystemValue.JWT_HEADER_VALUE_PREFIX + Claims claimsDecode = Jwts.parser().setSigningKey( "sad12f" ).parseClaimsJws(visitTK).getBody(); System.out.println(claimsDecode.getIssuer()); } } |
以上纯为个人总结,如有错误,还请指出,谢谢。
原文:https://www.cnblogs.com/yhc-910/p/14185636.html