需求分析
登录成功后,系统会返回一个token给客户端使用,token可以用来获取登录后的一些资源或者进行一些操作。当用户在系统中注销或者退出登录时,需要对token进行过期处理,以保证系统的安全性和数据的保护。以下是对登出成功后token过期需求分析的概述:
- 自动token过期:
当用户在系统中进行注销或者退出登录时,需要将该用户的token过期,禁止后续的使用。具体实现是,服务器应该在注销或退出登录时,将该用户token的状态设定为过期状态或者设置token的有效期为0,以保证token的及时失效。这种方式比手工管理token过期的方式高效、可靠并且节省时间和人力成本。- token黑名单机制:
当token失效后,服务器可以将这个token加入到一个黑名单中,以确保这个token失效不被误用。使用黑名单机制可以提升token失效之后的安全性,防止因失效而导致信息泄漏和数据损坏等问题。- 多种方式保证token的有效性:
系统应该设定一个较短、可控制的token有效期限。在该有效期内,当客户端通过传递请求头信息携带token请求资源或服务时,服务器应该及时响应。如果在该有效期内由于某种原因导致服务器未能及时响应,则客户端应该自动或人工更新或请求获取新的token。- 日志和监控:
需要对token过期和失效的记录进行审查和监测。通过监控系统,可以及时发现潜在的安全威胁,快速定位问题,并采取相应的措施,保障系统的安全性和稳定性。
以上是登出成功后token过期需求分析的一些基础内容,其他实际应用中还有更多的细节需要分析和处理。
解决方案
为了解决客户在
token
未过期期间退出登录,原颁发的token
还没有过期,我们采用redis
缓存未过期的token
(过期时间为该token
剩余过期时间),也可以把这种方式称为黑名单。
实现步骤
登出成功相关逻辑改造
由于我们集成了springsecurity,所以逻辑就写在了登出成功的拦截器中
具体实现步骤如下:
- 通过拦截器输入参数
HttpServletRequest
获取header
中的token
- 获取当前token的剩余过期时间
- 将该
token
添加到黑名单中,并设置过期时间为剩余过期时间
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Autowired
private RedisUtil redisUtil;
@Value("${jwt.authorization}")
private String authorization;
@Autowired
private JwtTokenUtils jwtTokenUtils;
@SneakyThrows
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication) {
//获取token
String token = httpServletRequest.getHeader(authorization);
...
//获取当前token剩余毫秒数
long expiration = jwtTokenUtils.getExpirationTimeMillisFromToken(token);
//添加token黑名单
redisUtil.setEx(
RedisConstant.EXPIRATION_TOKEN
.concat(CommonConstant.COLON)
.concat(token),
JSON.toJSONString(allClaimsFromToken),
expiration,
TimeUnit.MILLISECONDS);
...
}
携带token请求相关逻辑
由于我们集成了springsecurity,所以逻辑就写在了自定义的拦截器中,接口携带token请求时会进入该拦截器
具体实现步骤如下:
- 通过拦截器输入参数
HttpServletRequest
获取header
中的token
- 校验token是否在黑名单中
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtils jwtTokenUtils;
@Value("${jwt.authorization}")
private String authorization;
@Autowired
private IUserService userService;
@Autowired
private RedisUtil redisUtil;
@SneakyThrows
@Override
protected void doFilterInternal(HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain chain) {
//获取请求携带的token
final String token = request.getHeader(authorization);
//验证token是否为空
if (StringUtils.isNotBlank(token)) {
//验证token是否在黑名单中
if (!redisUtil.hasKey(RedisConstant.EXPIRATION_TOKEN.concat(CommonConstant.COLON).concat(token))) {
...
}
}
//继续执行
chain.doFilter(request, response);
}
}
![](https://resource.liulingfengyu.cn/img/扫码_搜索联合传播样式-标准色版压缩版.png)