前言
token的灵活使用是大家都有所了解的,但是如果指定了token的失效时间过短的话,会造成用户的使用体验大打折扣。假如设定了两小时,两小时内无论做什么动作,时间一到就会失效重新登录。这样太麻烦了,有没有可以让两小时内一旦点击内容或者做了某动作,就会让token的过期时间在那个时间点后面再延长两小时。答案是有的。现在我来说说如何实现。
1.当登录成功时,redis添加key和value值均为token的值
@RestController
public class TestLoginController {
/**
* 用户登录的接口,校验数据库之后,调用token工具类生成token返回客户端
*
* @param user 用户信息
* @return 返回的信息
*/
@Resource
private UserMapper1 userMapper1;
@Resource
private RedisUtils redisUtils;
@PostMapping(value = "/login",produces = {"application/json;charset=UTF-8"})
public HashMap<String,Object> login(@RequestBody String json){
JSONObject jsonObject = JSONObject.parseObject(json);
String name = jsonObject.getString("name");
String password = jsonObject.getString("password");
HashMap<String,String> claim = new HashMap<>();
claim.put("name",name);
User user = userMapper1.selectUserByName(name);
HashMap<String,Object> hashMap = new HashMap<>();
if (null == user ){
hashMap.put("rv",400);
hashMap.put("message","登录失败,用户不存在");
return hashMap;
}else{
//用户校验成功
String token = TokenUtil.createToken(claim);
redisUtils.set(token,token,20); //redis20秒过期
hashMap.put("token",token);
hashMap.put("rv",200);
hashMap.put("message", "认证成功");
}
return hashMap;
}
}
2.在拦截器配置REDIS续期
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Resource
RedisUtils redisUtils;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle");
//如果不是映射到方法直接放行
if(!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(PassToken.class)||
handlerMethod.getBeanType().getAnnotation(PassToken.class) != null)
{ //方法上面是否有注解?
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.value()){
return true;
}
}
HashMap<String,String> hashMap = new HashMap<>();
String token = request.getHeader("token");
try {
String redisToken = (String)redisUtils.get(token);
if(StringUtils.hasText(redisToken)){
Integer time = Math.toIntExact(redisUtils.getExpire(token));
if (time>0&&time<10){
System.out.println("续期redis");
redisUtils.expire(redisToken,20);
}
return true;
}
TokenUtil.verifyToken(token);
return true;
}catch (SignatureVerificationException e){
e.printStackTrace();
hashMap.put("msg","签名不一致");
}catch (TokenExpiredException e){
e.printStackTrace();
hashMap.put("msg","令牌过期异常");
}catch (AlgorithmMismatchException e) {
e.printStackTrace();
hashMap.put("msg", "算法不匹配异常");
}catch (InvalidClaimException e){
e.printStackTrace();
hashMap.put("msg","失效的claim异常");
}catch (Exception e){
e.printStackTrace();
hashMap.put("msg","token无效");
}
String resultJson = JSON.toJSONString(hashMap);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(resultJson);
return false;
}
}
2.1.主要看这一段
String token = request.getHeader("token");
try {
String redisToken = (String)redisUtils.get(token);
if(StringUtils.hasText(redisToken)){
Integer time = Math.toIntExact(redisUtils.getExpire(token));
if (time>0&&time<10){
System.out.println("续期redis");
redisUtils.expire(redisToken,20);
}
return true;
}
解释: 从请求头获取的token的值作为key,在redis查询该key的值。如果存在该key的值,那么抓取该key的过期时间,如果该key的过期时间小于10秒且大于0秒,则帮该key续期20秒。做time>0&&time<10判断过期的时间的原因,是免得redis频繁续期。然后返回true,不拦截该请求。
3.redisUtils工具类部分方法简述
@Resource
private RedisTemplate redisTemplate;
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒)time小于等于0,将设置无限期
* @return true 成功 false 失败
*/
public boolean set(String key,Object value,long time){
try {
if(time > 0){
redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
}else{
set(key,value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time > 0){
redisTemplate.expire(key,time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
}
项目地址
https://gitee.com/liuweiqiang12/springboot/tree/master/springboot-mybatis-token-redis