JWT全称JSON WEB TOKEN,是一种经过加密的包含非敏感信息且具有时效性的固定格式字符串
既然它是JSON,那么它必然满足JSON的格式;JWT由header、payload、signature三部分共同组成。
header : {"alg" : HS256,"typ" : JWT} 主要存储加密算法alg、数据类型typ等元数据
payload:主要存储需要进行传输的非敏感信息,如用户的名称、角色等
signature:由header与payload经过base64编码后得到的字符串,使用header中声明的加密算法对字符串进行加密后得到
作用:JWT的设计目的就是在服务器不存储用户信息、状态的情况下,安全的传递非敏感数据
应用场景:根据JWT的作用,我们可以在解决客户端访问服务器时的身份认证问题时引入JWT
JWT使用Base64编码方式对header、payload编码是众所周知的,那我们在截取了JWT后,进行反编码获取header中存储的加密算法,再使用相同算法伪造JWT访问服务器,服务器改如何辨别呢?
这时候就要引入密钥的概念了,在使用加密算法对编码后的header、payload进行加密时还使用了密钥,而密钥保证了只知道加密算法的情况下,无法对signature进行解密
我们可以把密钥想象成一个方法的参数、加密算法是一个方法,不同的传参,方法的执行结果或返回值肯定不同,而关键之处就在于:密钥只被服务器(也就是生成JWT的一端)持有
(1)传统cookie/session是在用户登录后,在服务器内存中保存一个session“会话”,“会话”里维护了用户相关信息;每个session生成后会有一个sessionID,服务器会将sessionID返回给客户端,客户端拿到sessionID后,接下来的每一次请求会把sessionID放在cookie中,由服务器通过sessionID查找对应session,对请求进行鉴权。
JWT虽然也是由服务器生成,但它不会保存在自己的内存中,而是由客户端进行保存,好处是服务器不会有内存溢出的风险。像cookie/session这种方式,在用户流量较大时,服务器的内存可能会溢出
(2)传统cookie/session无法解决集群模式下,不同服务器节点无法共享session的问题。每一台服务器的session都有属于自己的“域”,无法共享;如果用户在登录后,其用户信息被保留在集群的其中一台服务器节点中,它随后的请求被负载均衡到了其它服务器节点,由于session不能共享、用户也没有在其它服务器保存session,那么在访问一些需要登录的接口时,用户就需要重新登录,极大地影响用户体验
JWT不会存在上述问题,因为服务器获取到这个token之后进行JWT解析来读取用户数据,如果没有数据就代表没有登录,有数据且未失效,则身份认证通过,拦截器放行。
(1)依赖导入:
io.jsonwebtoken
jjwt
0.9.0
(2)配置文件(注意yaml格式):
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: 123
# 令牌有效期(默认30分钟)
expireTime: 30
(3)创建JWT:
JwtBuilder builder = Jwts.builder();
return builder
// header
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
// payload
.claim("username","tom")
.claim("userId","123")
// expire time
.setExpiration(new Date(System.currentTimeMillis + oneDat))
// signature
.signWith(SignatureAlgorithm.HS256,base64EncodeSecretKey) //加密算法和密钥
.compact();
(4)解析JWT:
try{
JwtParser parser = Jwts.parser();
Jws claimsJws = parser.setSigningKey(base64EncodedSecretKey).parseClaimsJws(jwt);
Claims claims = claimsJws.getBody();
println(claims.get("username"));
println(claims.getExpiration());
}catch(JwtException e){
//handle exception
}