一、流程
用户首次登录后,服务端给客户端发两个 token ,分别为 accessToken 和 refreshToken。
- accessToken 的生存时间较短,用于后续客户访问接口时使用。
- refreshToken 则用于更新 accessToken 并返回给客户端, 更新后 refreshToken 也需要刷新。
更新或生成accessToken时,需要更新用户所有的 refreshToken 的过期时间。
二、token 说明
- 过期时间【测试】
- accessToken :2天
- refreshToken :15天
- 存储信息:
-
accessToken:
-
- 用户基本信息:用户Id,用户的权限
-
refreshToken:
-
- 过期时间
- 用户id
- redis 缓存内容:
使用 hash 数据类型保存用户的token信息,因为一个用户可能会多地登录。
-
Key:【user_token : [userId]】
-
Value【hash】:
-
- key : accessToken
- value:
-
-
- refreshToken
- expireTime(refreshToken的过期时间)
-
三、社交登录
社交登录简单基本流程:【以gitee为例】
- 在第三方应用【gitee】中注册应用,获取 client_id 和 client_secret
- 引导用户到第三方【gitee】认证页面,此时请求带上 client_id
- 用户授权后,带着第三方【gitee】生成的 code 到回调地址。
- 服务端带着 code,client_id,client_secret,redirect_uri 信息,请求第三方【gitee】获取access_token
- 服务端带着 access_token 可以获取用户的信息
- 可以使用用户信息进行 注册/登录
四、问题及解决
1、token 过期后无法解析
使用以下 jwt 工具进行生成jwt和解析jwt。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
如果 解析过期的 token 会报错,抛出【io.jsonwebtoken.ExpiredJwtException】异常。
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2024-05-15T22:00:07Z.
Current time: 2024-05-15T22:00:56Z, a difference of 49868 milliseconds.
Allowed clock skew: 0 milliseconds.
所以过期后的 token 就没什么用了,我们无法从中获取到对应的refreshToken信息。
解决:
在redis 中存储该 accessToken 对应的 refreshToken,如果 accessToken 过期,就到redis 中获取 refreshToken ,解析 refreshToken,获取用户信息,并更新accessToken。
如果 refreshToken 也过期了,就提醒用户重新登录。
最后删除掉过期的 accessToken 对应的信息。
redis accessToken 和 refreshToken 映射信息:
key:【 access_refresh_mapping : accessToken】
value:refreshToken
2、若accessToken过期时,同时返回新的accessToken和响应数据
前提:这个accessToken对应的refreshToken没过期。
解决:
在过滤器中检测到accessToken过期但refreshToken没过期,生成新的accessToken,并把新的accessToken放到响应头中。
Claims claims = Jwts.parser()
.setSigningKey(REFRESH_SECRET_KEY)
.parseClaimsJws(refreshToken)
.getBody();
String newAccessToken = Jwts.builder()
.setSubject(claims.getSubject())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
response.setHeader("x-access-token", newAccessToken);
request.setAttribute("claims", claims);
3、feign 请求不用认证
微服务内部的请求不用请求了,那么如何辨别哪些请求是 feign 请求呢?
解决:
当发送 feign 请求前,可以在请求头加上 feign 请求相关标志。