redis优化token校验主动失效

  1. 场景:在普通的token颁发和校验中 当用户发现自己账号和密码被暴露了时修改了登录密码后旧的token仍然可以通过系统校验直至token到达失效时间,这肯定是不安全的,所以系统需要token主动失效的一种能力
  2. 解决方案:我们可以使用redis来实现redis主动失效的的功能
  3. 需求实现逻辑:
    1. 在每次用户登录后颁发token的同时往redis数据库中存储一份颁发给用户的token
    2. 每次每次用户请求时除了解析token外还需要查询redis中是否有当前token有则校验通过,没有则校验失败
    3. 每次用户修改密码后删除redis中当前用所携带的token,从而使旧token无法通过token校验
  4. 代码实现
    1. pom.xml中添加redis坐标
JavaScript 复制代码
<!--redis坐标-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
复制代码
2. 配置文件application.yml中配置redis相关信息
JavaScript 复制代码
spring:
  data:
    redis:
      host: redis所在的ip,本机的redis服务填localhost
      port: redis服务端口,默认6379
      password: redis中设置的密码,没有就不需要
复制代码
3. 颁发token时往redis中存储token
JavaScript 复制代码
//UserController中添加私有属性stringRedisTemplate并实例化
@Autowired
private StringRedisTemplate stringRedisTemplate;

//登录接口中把token存到redis 过期时间3小时
stringRedisTemplate.opsForValue().set(token,token,3, TimeUnit.HOURS);
复制代码
4. 登录时校验是否redis中有当前token
JavaScript 复制代码
package org.example.intercopters;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.utils.JwtUtil;
import org.example.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Map;

@Component //注册拦截器 将其放入ioc容器中
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate redisTemplate;
    //创建登录身份校验拦截器
    @Override  //请求开始前触发的拦截方法
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //令牌验证
        String token = request.getHeader("Authorization");
        //去除token的前缀标记"Bearer "
        var newToken = token.contains("Bearer ")?token.substring("Bearer ".length()):token;

        try {
            **//从redis获取相同的token
            String redisToken = redisTemplate.opsForValue().get(newToken);
            if(redisToken == null){
                //redis失效
                throw new RuntimeException("token失效");
            }**
            Map<String, Object> claims = JwtUtil.parseToken(token);

            //把用户信息存储到ThreadLocal中,tomcat会在每次接口请求时创建一个线程 而ThreadLocal中存储的数据是线程安全的
            ThreadLocalUtil.set(claims);

            //放行
            return true;

        } catch (Exception e) {
            //设置响应状态码
            response.setStatus(401);
            //设置响应字符集和响应内容
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html; charset=UTF-8");
            String errorMessage = "未登录";
            response.getWriter().write("{\"error\": \"" + errorMessage + "\"}");
            //不放行
            return false;
        }
    }
    @Override  //请求完成后触发的拦截方法
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清空ThreadLocal中的数据
        ThreadLocalUtil.remove();
    }
}
复制代码
5. 修改密码后删除redis中的token
JavaScript 复制代码
//修改密码接口中删除redis中对应的token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.getOperations().delete(newToken);
  1. 在本地开发中如果使用本地redis每次开发前还得去启动本地redis,就很麻烦,如果你有一个云服务器就可以本地连云服务器redis就不用每次去启动redis了,下面是实现教程
    1. https://blog.csdn.net/weixin_45695974/article/details/123244011
相关推荐
xuxie1337 分钟前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
白鹭37 分钟前
MySQL源码部署(rhel7)
数据库·mysql
重生成为编程大王1 小时前
Java中的多态有什么用?
java·后端
666和7771 小时前
Struts2 工作总结
java·数据库
还听珊瑚海吗1 小时前
SpringMVC(一)
数据库
中草药z1 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
野犬寒鸦1 小时前
力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
java·数据结构·算法·leetcode·面试
zru_96021 小时前
centos 系统如何安装open jdk 8
java·linux·centos
LiRuiJie2 小时前
深入剖析Spring Boot / Spring 应用中可自定义的扩展点
java·spring boot·spring