JSON Web Token(JWT)是目前最流行的跨域认证解决方案。
跨域认证问题及传统解决方案
-
传统认证流程
- 用户向服务器发送用户名和密码。
- 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
- 服务器向用户返回一个 session_id,写入用户的 Cookie。
- 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
- 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
存在的问题:
拓展性不好,单机可以,但是服务器集群,就要求session共享,每台服务器都能读取session
传统解决方案:session数据持久化,写入数据库或别的持久层。
-
JWT解决方案
- 用户登陆时,服务端加密由用户信息和过期时间组成的字符串,得到token,发送给客户端
- 客户端每次发送请求时都携带token
- 服务端解密或验证token,从而得到用户信息和会话过期时间。
Token结构
image.jpeg- Header(头部):内部是一个JSON对象,描述JWT的元数据
- Payload(负载):用来存放实际需要传递的数据。官方提供了七个字段,如iss、exp等,当然你也可以定义私有字段。但是要注意此处是不加密的,任何人都可以读取。
- Signature(签名):它是对前两部分的签名。防止数据被篡改。首先需要一个密钥,这个密钥只有服务器才知道,然后使用Header里指定的签名算法算出签名。
JWT的特点
- JWT默认不加密
- JWT不仅可以用于认证,也可以用于交换信息
- 最大的缺陷:由于服务器不保存session状态,因此无法在使用过程中废止或修改某个Token,也就是说,一旦签发了,在该Token到期前会始终有效,除非服务器部署额外的逻辑。
- JWT本身包含认证信息,一旦泄漏,任何人都可以获得该令牌的所有权限,为了减少盗用,JWT有效期应当设置的比较短。
Token生成代码
// 生成jwt访问令牌
String jwtToken = Jwts.builder()
// 用户角色
.claim("ROLE_LOGIN", "ADMIN")
// 主题 - 存用户名
.setSubject(“Ken")
// 过期时间 - 30分钟
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
// 加密算法和密钥
.signWith(SignatureAlgorithm.HS512, "helloworld")
.compact();
我自定义的JWT工具类
/**
* 检测token是否过期
* @param claims
* @return true:已过期,false:未过期
*/
public static boolean isJwtExpired(Claims claims) {
return claims.getExpiration().before(new Date());
}
/**
* 解析token
* @param token
* @param singKey
* @return claims
*/
public static Claims parsingToken(String token,String singKey){
return Jwts.parser()
.setSigningKey(singKey)
.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX,"")).getBody();
}
/**
* 返回username
* @param claims
* @return 当前token的username
*/
public static String getUsernameFromToken(Claims claims){
return claims.getSubject();
}
/**
* 返回token中的权限List
* @param claims
* @return List<SimpleGrantedAuthority>
*/
public static List<SimpleGrantedAuthority> getAuthFromToken(Claims claims){
return ((List<?>) claims
.get("rolue")).stream()
.map(authority -> new SimpleGrantedAuthority((String) authority ))
.collect(Collectors.toList());
}
网友评论