资讯动态

AI翻译助手-JWT与redis无状态认证

发布时间:2026/10/24 4:06:04 来源:尧图企业网站定制
既用 JWT 又查 Redis这个认证方案不矛盾学认证的时候都会遇到一个选择题JWT 无状态服务端不存 session天然可扩展但没法主动失效踢人下线、改密码让旧 token 失效都做不到Session 有状态可主动失效但要维护状态、分布式要共享。我在这套系统里选了个「看似矛盾」的组合——既用 JWT又用 Redis 存登录态。很多人会问JWT 不是无状态的吗你查 Redis 不是又变回有状态了没错这正是我想要的。这篇说清楚为什么。JWT 负责「证明你是谁」JWT 的核心作用是安全地传递身份信息服务端不用存储就能验证ComponentpublicclassJwtUtil{privatestaticSecretKeyKEY;publicJwtUtil(Value(${app.jwt.secret})Stringsecret){byte[]keyBytessecret.getBytes(StandardCharsets.UTF_8);if(keyBytes.length32){thrownewIllegalArgumentException(app.jwt.secret 长度必须至少 32 字节);}KEYKeys.hmacShaKeyFor(keyBytes);}publicstaticStringgenerateToken(Stringsubject,longttlMillis,MapString,Objectclaims){returnJwts.builder().setSubject(subject).addClaims(claims).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()ttlMillis)).signWith(KEY,SignatureAlgorithm.HS256).compact();}publicstaticClaimsparseToken(Stringtoken){returnJwts.parserBuilder().setSigningKey(KEY).build().parseClaimsJws(token).getBody();}}两个安全细节密钥从配置注入绝不硬编码生产用JWT_SECRET环境变量覆盖启动时校验密钥长度HS256 要求至少 32 字节不足直接启动失败把配置错误挡在启动期而不是线上才炸。Token 有效期设 7 天用户不用频繁登录。Redis 负责「你还能不能用」JWT 一旦签发服务端就没法让它作废除非等它过期。这在很多场景下不可接受用户改密码希望旧 token 立即失效、管理员想踢掉某个用户、用户主动退出登录。所以我用 Redis 补上「主动失效」的能力。登录时把 token 作为 key 存进 Redisauth:token:{token}value 是用户信息 JSONTTL 7 天和 JWT 一致。校验时不仅验 JWT还要查 RedispublicclassAuthTokenInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){StringtokenextractToken(request);if(tokennull){unauthorized(response,Missing token);returnfalse;}// ① 验 JWT签名 过期ClaimsclaimsJwtUtil.parseToken(token);if(JwtUtil.isExpired(claims)){unauthorized(response,Token expired);returnfalse;}// ② 查 Redistoken 是否还「活着」StringuserJsonredisTemplate.opsForValue().get(auth:token:token);if(userJsonnull){unauthorized(response,Session expired);returnfalse;}// ③ 还原用户写入上下文UseruserobjectMapper.readValue(userJson.getBytes(),User.class);UserContext.set(user);returntrue;}}登出的时候直接删掉 Redis 里的 keytoken 立即失效——这是 JWT 单靠自身做不到的。Token 怎么传我支持两种方式任选其一。Header 方式适合前后端分离、小程序、App 调 APICookie 方式适合浏览器端服务端渲染页面我的 Thymeleaf 页面就靠这个privateStringextractToken(HttpServletRequestrequest){Stringheaderrequest.getHeader(Authorization);if(header!nullheader.startsWith(Bearer )){returnheader.substring(7);}if(request.getCookies()!null){for(varc:request.getCookies()){if(auth_token.equals(c.getName())){returnc.getValue();}}}returnnull;}一个最容易泄漏的坑拦截器里用了UserContext本质是ThreadLocal保存当前用户。这里有个必须注意的点——ThreadLocal 用完必须清理UserContext.set(user);// 存入 ThreadLocalOverridepublicvoidafterCompletion(...){UserContext.clear();// 必须清理}因为 ThreadLocal 的生命周期和线程绑定而 Spring 的请求处理线程是从线程池复用的。如果不清理下一个请求复用同一个线程就会读到上一个请求的用户信息——这在「串号」场景下是严重的安全事故A 用户看到了 B 用户的数据。所以铁律是ThreadLocal 用完必须 clear且要在afterCompletion无论成功失败都执行里做。放行路径拦截器不是一刀切全拦要精确放行不需要鉴权的路径if(uri.startsWith(/static/)||uri.startsWith(/css/)||uri.startsWith(/js/)||uri.startsWith(/images/)||uri.startsWith(/login)||uri.startsWith(/api/auth/)){returntrue;}静态资源css/js/图片和登录、注册、验证码这些「还没登录就能用」的接口必须放行否则用户连登录都登不进去。「JWT Redis」这套混合方案本质是各取所长JWT 无状态地证明身份、可扩展Redis 补上主动失效能力ThreadLocal 在请求内传递当前用户。安全上记住三条就够了——密钥不硬编码、ThreadLocal 用完必 clear、放行路径精确。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑