本文结束啦感谢您的阅读
JWT的优点
由于JWT本身是无状态的,仅仅是存储登录授权的信息,可以实现单点登录的功能,因为在多个系统中只要在实现jwt token的时候用同一个密钥进行加密,则可以实现跨平台而登陆。
JWT的缺点
==token泄露问题==:由于只要拿到token就可以在不同机器上进行登录,因此只要token泄露出去后用户可以通过登录后改变密码进行窃取。
==token续签问题==:jwt虽然内置了token过期的功能,但是并没有提供token续签的问题,如果用户在进行重要信息的存储时,刚好token过期了,则导致用户需要重新登录,因此容易造成重要信息丢失,影响客户体验。该项可以通过设计各种策略刷新token过期时间,而session这方面就比jwt好很多,session会自动刷新过期时间,例如是30min过期,但用户在30min内登录session后重新计算过期时间。
JWT token构成
一个token包含三个部分(Header,Payload,Signature)
header 头部
标头通常由两部分组成:令牌的类型,即JWT,以及哈希算法,如HMAC SHA256或RSA。1
2
3
4{
"alg": "HS256",
"typ": "JWT"
}
HS256 表示使用了 HMAC-SHA256 来生成签名。接下来对这部分内容使用 Base64Url编码组成了JWT结构的第一部分。
Payload 载荷
payload包含三种类型的claim:reserved, public 和 private1
2
3
4
5{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有1
2
3
4
5
6
7iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
Public claims:根据需要定义自己的字段,注意应该避免冲突
Private claims:这些是自定义的字段,可以用来在双方之间交换信息
上述的负载需要经过Base64Url编码后作为JWT结构的第二部分。
Signature 签名
jwt的第三部分是一个签证信息,这个签证信息算法如下:
base64UrlEncode(header) + "." + base64UrlEncode(payload)+your-256-bit-secret1
2
3
4HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
JWT简单案例
引入依赖包1
2
3
4
5
com.auth0
java-jwt
3.4.0
案例代码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
135package jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
* JwtTokenUtil
*
* Description
*
* Creation Time: 2018910 0010 12:03.
*
* @author virvil
* @since jwtdemo 0.0.1
*/
public class{
* The constant logger.
*/
private static Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);
* The constant properties.
*/
private static Properties properties = new Properties();
* The constant signkey.
*/
private static String signkey = null;
* The constant algorithm.
*/
private static Algorithm algorithm = null;
/*
加载配置文件内容
*/
static {
InputStream inputStream = JwtTokenUtil.class.getClassLoader().getResourceAsStream("jwt.properties");
try {
properties.load(inputStream);
String signkey = properties.getProperty("signkey");
algorithm = Algorithm.HMAC256(signkey);
} catch (IOException e) {
logger.error("读取配置文件异常",e);
}
}
* Get token string.
*生成token
* @param username the username
* @return the string
*/
public static String getToken(String username){
Calendar calendar = Calendar.getInstance();
//签发时间
Date issueDate = calendar.getTime();
//过期时间
long expires = issueDate.getTime()+Integer.parseInt(properties.getProperty("timeout"));
Date expiresDate = new Date(expires);
//header
Map header = new HashMap<>();
header.put("alg","HS256");
header.put("typ","JWT");
String token = JWT.create()
.withHeader(header) //设置header头信息
.withClaim("iss","auth0") //设置payload
.withClaim("username",username)
.withIssuedAt(issueDate)
.withExpiresAt(expiresDate)
.sign(algorithm);
return token;
}
* Verify token map.
*对token进行解密
* @param token the token
* @return the map
*/
public static Map verifyToken(String token){
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT verify = null;
try {
verify = jwtVerifier.verify(token);
return verify.getClaims();
}catch (TokenExpiredException ex){
logger.error("token已经过期",ex);
}catch (Exception e){
logger.error("登录异常",e);
}
return null;
}
* The entry point of application.
*
* @param args the input arguments
*/
public static void main(String[] args){
String username = "virvil";
String token = getToken(username);
//验证token是否正确
Map claims = verifyToken(token);
if (claims!=null){
Claim usernameClaim = claims.get("username");
//此处获得的信息可以到数据库中进行查询,验证是否存在该用户信息,这里我只是简单测试,莫怪
System.out.println(username.equals(usernameClaim.asString())?"token验证成功":"token验证失败");
}
//用一个过期的token来验证结果
String token2 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
".eyJpc3MiOiJhdXRoMCIsImV4cCI6MTUzNjU2MTUzNSwiaWF0IjoxNTM2NTYxNTM0LCJ1c2VybmFtZSI6InZpcnZpbCJ9" +
".JNNCERaaApOaoNYV45liZTJ-mF7UIRLNz-EY5tAPC4Q";
verifyToken(token2);
}
}