SpringBoot18-集成Redis

发布时间:2026/7/23 19:38:41
SpringBoot18-集成Redis 一、SpringBoot集成Redis1-1、先理解什么是 RedisRedis 就是一个超快的数据库但它专门存数据在内存里不是硬盘。你可以把它想象成一个超级快的“笔记本”用来暂时记一些数据。比如登录状态、验证码、排行榜、购物车、点赞数……为什么用 Redis因为它速度非常快比 MySQL 快几十~上百倍可以自动过期适合存验证码、token可以解决并发问题比如抢票、库存扣减1-2、第一步安装 Redis1如果你用Windows下载 Redis for Windows无需安装https://github.com/tporadowski/redis/releases打开redis-server.exe就启动了。2如果是Macbrew install redis brew services start redis启动成功后Redis 会在6379端口上运行。3验证是否安装了redis1-3、第二步在 Spring Boot 项目中加依赖在pom.xml加dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency就这样Redis 支持就进项目了。1-4、第三步配置连接 Redis在application.yml里写spring: data: redis: host: 127.0.0.1 # 本机 port: 6379 # 默认端口如果你 Redis 设置了密码这里再加password: xxx1-5、第四步写一个最简单的 Redis 代码存 取1、写一个 Service 类Service public class RedisTestService { Autowired private StringRedisTemplate stringRedisTemplate; // 专门操作字符串 // 存数据 public void saveData() { stringRedisTemplate.opsForValue().set(name, Tom); } // 取数据 public String getData() { return stringRedisTemplate.opsForValue().get(name); } }Maven中添加了redis的依赖之后StringRedisTemplate就能直接注入使用了2、写个 Controller 调用它RestController public class TestController { Autowired private RedisTestService redisTestService; GetMapping(/save) public String save() { redisTestService.saveData(); return 保存成功; } GetMapping(/get) public String get() { return redisTestService.getData(); } }3、运行测试浏览器访问① 保存数据http://localhost:8080/save② 获取数据http://localhost:8080/get你将看到返回Tom你已经成功用 Spring Boot 操作 Redis 了4、或者直接写一个redis的测试类1-6、小结二、令牌主动失效目标登录成功 → 把发给浏览器的令牌token同时存进 Redis每次请求在拦截器里比对“浏览器带来的令牌 Redis里为该用户保存的令牌”用户改密成功 → 主动删除 Redis 里的旧令牌使其立刻失效。2-1、Redis 存储设计双向映射便于比对和失效auth:user:{userId} - {token}用户当前有效 token便于改密时一把踢下线auth:token:{token} - {userId}用 token 找到用户拦截器校验时用两个 key 都设置相同TTL比如 2 小时再次登录会覆盖旧 token。2-2、登录生成并落库含顶替旧令牌Service RequiredArgsConstructor public class AuthService { private final StringRedisTemplate redis; // 令牌有效期 private static final long TOKEN_TTL_MINUTES 120; public String login(Long userId, String rawPassword) { // 1. 这里省略账号密码校验查库比对哈希 // if (!passOK) throw new BizException(...); // 2. 生成新 token String token UUID.randomUUID().toString(); // 3. 顶替旧 token如果有就删 String oldToken redis.opsForValue().get(auth:user: userId); if (oldToken ! null) { redis.delete(auth:token: oldToken); } // 4. 双向写入 过期时间 redis.opsForValue().set(auth:user: userId, token, TOKEN_TTL_MINUTES, TimeUnit.MINUTES); redis.opsForValue().set(auth:token: token, String.valueOf(userId), TOKEN_TTL_MINUTES, TimeUnit.MINUTES); // 5. 返回给前端 return token; } // 退出登录前端丢弃 服务端删除 public void logout(String token) { if (token null) return; String userId redis.opsForValue().get(auth:token: token); if (userId ! null) { redis.delete(auth:token: token); redis.delete(auth:user: userId); } } // 改密后让旧令牌立即失效主动删除 public void invalidateUserTokenOnPasswordChange(Long userId) { String oldToken redis.opsForValue().get(auth:user: userId); if (oldToken ! null) { redis.delete(auth:user: userId); redis.delete(auth:token: oldToken); } } }示例 Controller返回 token 给前端退出改密后失效RestController RequiredArgsConstructor public class AuthController { private final AuthService authService; PostMapping(/login) public String login(RequestParam Long userId, RequestParam String password) { return authService.login(userId, password); } PostMapping(/logout) public void logout(RequestHeader(Authorization) String token) { authService.logout(token); } // 改密成功后调用旧 token 立刻失效 PostMapping(/password/change) public String changePwd(RequestParam Long userId, RequestParam String oldPwd, RequestParam String newPwd) { // 1) 校验旧密码并更新为新密码略 // 2) 主动失效旧令牌 authService.invalidateUserTokenOnPasswordChange(userId); // 3) 可选直接让用户重新登录或这里返回新 token return OK; } }2-3、拦截器校验比对“请求令牌 Redis 存的令牌”Component RequiredArgsConstructor public class LoginInterceptor implements HandlerInterceptor { private final StringRedisTemplate redis; Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { String token req.getHeader(Authorization); if (token null || token.isEmpty()) { resp.setStatus(401); return false; } // 1) 通过 token 找 userId无则说明 token 无效或过期 String userId redis.opsForValue().get(auth:token: token); if (userId null) { resp.setStatus(401); return false; } // 2) 再用 userId 取当前有效 token必须“完全相等”才算有效 String currentToken redis.opsForValue().get(auth:user: userId); if (!token.equals(currentToken)) { // 出现这种情况用户在别处重新登录/改了密码/已被踢下线 resp.setStatus(401); return false; } // 校验通过 return true; } }注册拦截器Configuration RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login); // 登录接口放行 } }2-4、前端怎么带令牌登录后保存服务端返回的token之后每个请求都在请求头带上Authorization: token名字可自定义统一即可2-5、可选增强自动续期活跃会话不掉线在拦截器校验通过后给两个 key 续命重置 TTL// 校验通过后 redis.expire(auth:token: token, 120, TimeUnit.MINUTES); redis.expire(auth:user: userId, 120, TimeUnit.MINUTES);单设备登录上面的“顶替旧令牌”已经实现了单设备登录——新登录会让旧 token 失效。多端共存把auth:user:{userId}改成 Set允许保存多个 tokenPC/手机各一个改密时删除该用户所有 token。