后端redisjwtredis二次校验实现jwt主动“失效”
caolibin
问题:jwt令牌一旦生成,就不能再更改,有时候想让令牌提前失效该怎么办?
解决方案:使用redis对token进行二次校验,由redis来管理token的过期时间
1.保存token到redis
在用户登录方法中,生成token,并保存一份到redis中
@Override public Result<ReaderVo> login(LoginDto reader) { Reader r = readerMapper.selectByName(reader.getUsername()); if (r == null) { return Result.error(Excep.USER_NOT_EXIST); } String pwd = r.getPassword(); if (!pwd.equals(reader.getPassword())) { return Result.error(Excep.WRONG_PASSWORD); }
Map<String, Object> claims = new HashMap<>(); claims.put(Common.ID, r.getId()); claims.put(Common.USERNAME, reader.getUsername()); String token = JwtUtils.generateJwt(claims);
redisTemplate.opsForValue().set(token, token, Jwt.EXPIRE_TIME);
ReaderVo readerVo = new ReaderVo(); readerVo.setToken(token); BeanUtils.copyProperties(r, readerVo);
return Result.success(readerVo); }
|
2.二次校验token
在jwt拦截器中,校验redis中是否存在token,如果不存在,说明token已经过期
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!(handler instanceof HandlerMethod)) { return true; }
String token = request.getHeader(Common.TOKEN); log.debug("token:{}", token); if (!StringUtils.hasLength(token)) { log.error(Excep.TOKEN_NOT_EXIST); response.setStatus(Code.NOT_LOGIN_CODE); return false; }
try { String redisToken = redisTemplate.opsForValue().get(token); if (redisToken == null) { throw new BaseException(Excep.TOKEN_ALREADY_EXPIRED); } Claims claims = JwtUtils.parseJWT(token); ThreadLocalUtil.set(claims); String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(claims.getExpiration()); log.debug("id:{},username:{},令牌到期时间:{}", claims.get(Common.ID), claims.get(Common.USERNAME), format); } catch (Exception e) { String message = e.getMessage(); log.error("非法令牌 " + message); if (message.contains("expire")) { log.error("令牌过期!"); } response.setStatus(Code.IDENTITY_EXPIRES); return false; } return true; }
|
3.创建删除token接口
创建一个logout退出登录接口,前端退出登录时调用此接口,将token从redis中删除
@DeleteMapping("/logout") public Result<String> logout(@RequestHeader(Common.TOKEN) String token) { log.debug("token:{}", token);
redisTemplate.delete(token); return Result.success(); }
|
4.前端调用
4.1 定义登出接口,调用后端
import request from "@/util/request.js";
const logoutService = function () { return request.delete('/logout'); }
export {logoutService}
|
4.2 登出功能中调用上面接口
const logout = async () => { await logoutService(); ElMessage.success("已退出登录!") tokenStore.setToken(null); readerStore.clearReader(); adminStore.clearAdmin(); await router.push("/login"); };
|
5.联调测试