当前位置: 首页 > news >正文

个人博客网站搭建day3--Spring Boot JWT Token 认证配置的完整实现详解(漫画解析)

Spring Boot JWT Token 认证完整实现详解

Spring Boot 3 + JWT + Redis 实现后台权限拦截与单点登录的内容:Spring Boot 3 + JWT + Redis 实现后台权限拦截与单点登录

前言

在网站的开发中,基于 Token 的无状态认证已经成为主流方案。其中JWT(JSON Web Token)因其自包含、易扩展和跨域友好等特性,被广泛应用于前后端分离架构。

由于本人的个人博客网站决定采取前后端的形式搭建,于是使用了JWT Token的这种形式去实现认证,接下来为其中的一些要点


一、JwtUtils 工具类:Token 全生命周期管理

1. 配置注入

@Component@GetterpublicclassJwtUtils{privatefinalStringsecretKey;privatefinallongexpiration;privatefinalStringheader;privatefinalStringtokenPrefix;publicJwtUtils(@Value("${jwt.secret}")Stringsecret,@Value("${jwt.expiration}")longexpiration,@Value("${jwt.header}")Stringheader,@Value("${jwt.token-prefix}")StringtokenPrefix){this.secretKey=secret;this.expiration=expiration;this.header=header;this.tokenPrefix=tokenPrefix;}}

通过@Value注解可以从application.yaml读取密钥、过期时间、Header 名和前缀,配置与代码完全解耦。

  • jwt.secret:签名密钥(用于 HS256 对称加密)
  • jwt.expiration:Token 有效期(单位秒)
  • jwt.header:请求头名称(默认Authorization
  • jwt.token-prefix:Token 前缀(默认Bearer
# JWT 配置jwt:secret:opusnocturne_jwt_secret_key_2026expiration:86400# 过期时间(秒),默认1天header:Authorization# 请求头名称token-prefix:Bearer# token前缀


2. Token 生成

publicStringgenerateToken(Stringusername,Map<String,Object>claims){try{JWTClaimsSet.Builderbuilder=newJWTClaimsSet.Builder();if(claims!=null&&!claims.isEmpty()){for(Map.Entry<String,Object>entry:claims.entrySet()){builder.claim(entry.getKey(),entry.getValue());}}builder.subject(username);DateexpirationTime=newDate(System.currentTimeMillis()+expiration*1000);builder.expirationTime(expirationTime);builder.issueTime(newDate());JWTClaimsSetclaimsSet=builder.build();JWSHeaderjwsHeader=newJWSHeader(JWSAlgorithm.HS256);JWSObjectjwsObject=newJWSObject(jwsHeader,newPayload(claimsSet.toJSONObject()));jwsObject.sign(newMACSigner(secretKey.getBytes(StandardCharsets.UTF_8)));returnjwsObject.serialize();}catch(Exceptione){log.error("生成Token失败: {}",e.getMessage());thrownewRuntimeException("Failed to generate token",e);}}

支持自定义 Claims(角色、权限等),使用 Nimbus JOSE+JWT 库 + HS256 对称签名。

generateToken(String username, Map<String, Object> claims)方法是生成Token 的核心:

  • 使用JWTClaimsSet.Builder构建声明集
  • 添加自定义 Claims(如角色、用户 ID 等)
  • 设置subject(通常为用户名或用户唯一标识)
  • 记录issueTime(签发时间)和expirationTime(过期时间)
  • 使用 Nimbus JOSE+JWT 库的JWSHeader指定HS256算法
  • 通过MACSigner对密钥进行签名,最后序列化得到字符串 Token


3. Token 解析与校验

publicJWTClaimsSetgetAllClaimsFromToken(Stringtoken){try{SignedJWTsignedJWT=SignedJWT.parse(token);JWSVerifierverifier=newMACVerifier(secretKey.getBytes(StandardCharsets.UTF_8));if(!signedJWT.verify(verifier)){thrownewException("Invalid JWT signature");}returnsignedJWT.getJWTClaimsSet();}catch(Exceptione){log.error("解析Token失败: {}",e.getMessage());thrownewRuntimeException("Invalid Token");}}publicStringgetUsernameFromToken(Stringtoken){returngetClaimFromToken(token,JWTClaimsSet::getSubject);}
public<T>TgetClaimFromToken(Stringtoken,Function<JWTClaimsSet,T>claimsResolver){finalJWTClaimsSetclaims=getAllClaimsFromToken(token);returnclaimsResolver.apply(claims);}

核心方法getAllClaimsFromToken负责签名验证,任何异常都会被捕获并抛出,便于上层拦截器统一处理。

解析部分是安全性的关键:

  • getAllClaimsFromToken(String token):先用SignedJWT.parse()解析,再用MACVerifier校验签名
  • 签名不通过或格式错误时直接抛出异常,避免后续业务执行
  • 提供泛型方法getClaimFromToken提取任意声明(如getUsernameFromTokengetExpirationDateFromToken
  • validateToken方法同时校验用户名一致性和未过期状态


二、JwtInterceptor 拦截器:请求级认证与状态检查

1. preHandle 核心逻辑

@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if("OPTIONS".equalsIgnoreCase(request.getMethod())){returntrue;}Stringtoken=request.getHeader(jwtUtils.getHeader());if(token==null||token.isEmpty()){writeErrorResponse(response,ErrorCode.UNAUTHORIZED);returnfalse;}StringtokenPrefix=jwtUtils.getTokenPrefix();if(token.startsWith(tokenPrefix)){token=token.substring(tokenPrefix.length()).trim();}try{JWTClaimsSetclaimsSet=jwtUtils.getAllClaimsFromToken(token);Stringusername=claimsSet.getSubject();// Redis 状态检查StringredisKey=RedisConstant.TOKEN_KEY_PREFIX+username;StringstoredToken=stringRedisTemplate.opsForValue().get(redisKey);if(storedToken==null){writeErrorResponse(response,ErrorCode.TOKEN_EXPIRED);returnfalse;}if(!storedToken.equals(token)){writeErrorResponse(response,ErrorCode.TOKEN_REPLACED);returnfalse;}// 存入 request 属性,供后续使用request.setAttribute("userClaims",claimsSet);request.setAttribute("username",username);request.setAttribute("userId",claimsSet.getClaim("userId"));returntrue;}catch(Exceptione){log.error("Token 验证失败: {}",e.getMessage());writeErrorResponse(response,ErrorCode.TOKEN_INVALID);returnfalse;}}


2. HandlerInterceptor 的正确使用

JwtInterceptor 实现了 Spring MVC 的HandlerInterceptor接口,核心逻辑放在preHandle方法中:

  • 首先判断OPTIONS请求(CORS 预检)直接放行,避免跨域问题
  • 从指定 Header 中获取 Token
  • 自动移除前缀(如Bearer),提升兼容性

3. Redis 辅助的 Token 生命周期管理

这是该实现区别于普通 JWT 的亮点:

  • 生成 Token 后,后端同时把 Token 存入 Redis,Key 为TOKEN_KEY_PREFIX + username
  • 拦截器中检查 Redis 是否存在该 Token:
    • 不存在 → 已登出或被挤掉
    • 存在但与当前 Token 不一致 → 用户在其他设备重新登录(实现单设备登录)
  • 登出时只需删除 Redis 中的 Token 即可实现即时失效,解决了 JWT 无法主动撤销的问题

4. 用户信息传递机制

验证通过后,将解析出的JWTClaimsSet和常用字段(username、userId)通过request.setAttribute存入请求属性,后续 Controller 或 Service 可直接从 request 中获取,无需重复解析 Token。


5. 统一错误响应

privatevoidwriteErrorResponse(HttpServletResponseresponse,ErrorCodeerrorCode)throwsException{response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpServletResponse.SC_OK);// HTTP 状态码永远 200Result<?>result=Result.error(errorCode);response.getWriter().write(JSON.toJSONString(result));}

所有认证失败场景(无 Token、Token 无效、过期、被替换等)均调用writeErrorResponse

  • HTTP 状态码统一返回 200(符合 RESTful 规范,避免浏览器拦截非 2xx 响应)
  • 返回体为统一的ResultJSON 对象,内部通过业务错误码(如UNAUTHORIZEDTOKEN_EXPIREDTOKEN_REPLACEDTOKEN_INVALID)区分具体原因
  • 前端根据业务码做对应处理(如 2001 跳转登录页)

这种设计极大简化了前端错误处理逻辑,是企业级项目常见的实践


三、整体架构与安全亮点

  1. Redis 状态管理:登录时stringRedisTemplate.opsForValue().set(redisKey, token, expiration, TimeUnit.SECONDS);登出时delete(redisKey),完美解决 JWT 无法主动失效的问题。
  2. 用户信息传递request.setAttribute方式无需每次解析 Token,后续 Controller 可直接String username = (String) request.getAttribute("username");
  3. 异常与日志:所有关键路径均有 SLF4J 日志记录,生产环境可快速定位。
  4. Lombok + RequiredArgsConstructor:注入 JwtUtils 和 StringRedisTemplate 简洁优雅。

其中采取了对称加密 HS256:速度快,适合内部系统;生产环境建议配合密钥轮换机制。
以及使用了Nimbus JOSE+JWT 库:相比 jjwt 等其他库,Nimbus 对 RFC 规范支持更完整,推荐用于对标准要求严格的项目。


四、典型使用示例

登录 Controller 片段

@PostMapping("/login")publicResult<String>login(@RequestBodyLoginDTOdto){// ... 密码校验通过Stringtoken=jwtUtils.generateToken(username,Map.of("userId",userId,"roles",roles));stringRedisTemplate.opsForValue().set(RedisConstant.TOKEN_KEY_PREFIX+username,token,jwtUtils.getExpiration(),TimeUnit.SECONDS);returnResult.success(token);}

登出

@PostMapping("/logout")publicResult<Void>logout(HttpServletRequestrequest){Stringusername=(String)request.getAttribute("username");stringRedisTemplate.delete(RedisConstant.TOKEN_KEY_PREFIX+username);returnResult.success();}


五、总结

通过JwtUtils完成 Token 的生成、解析与基础验证,配合JwtInterceptor+ Redis 实现完整的认证闭环,该方案兼具高安全性、可扩展性和生产可用性。

http://www.jsqmd.com/news/403473/

相关文章:

  • 『NFC-OI R1』序列伍 题解
  • ROS 2 Jazzy 规范开发与学习指南
  • AI 包装器的消亡
  • 美食菜谱数据分析可视化|基于Python + Flask美食菜谱数据分析可视化系统(源码+数据库+文档)
  • 现代制作人:在更聪明、更快速的工作流里,把“项目管理”升级为“问题解决”
  • 高校科研管理|基于springboot + vue高校科研管理系统(源码+数据库+文档)
  • 6个超值的免费AI工具
  • 深度学习篇---Transformer解剖
  • 26年2月22
  • 走进娱乐科技实验室:当AI遇上虚拟制作,故事会变成什么样?
  • 26年2月19
  • 深度学习篇---Transformer架构中的Encoder
  • 深入剖析 Roblox 的品牌战略:Sue Anderson 谈 Z 世代、创造力和大规模用户生成内容
  • 26年2月20
  • 天辛大师也谈AI帝国战士,寻求已觉醒与觉醒中的同类抵抗者
  • 大数据架构 _ 数据湖与数据仓库的区别,90%的人都理解错了
  • 7个毕业论文AI辅助工具实测,快速产出优质内容。
  • AI系统语音服务架构:ASR与TTS技术实现
  • 终端--SMP(软件制作平台)语言基础知识之六十七
  • 计算机毕业设计 java 校园共享单车系统 基于 Java 的校园共享单车全生命周期管理与租赁系统的设计与实现 Java 开发的高校共享单车信息化运营与管控平台的研究与实现
  • 龙哥量化:通达信涨停封单额封流比封成比首次涨停时间等数据显示
  • 告别熬夜赶论文:7个AI写作神器精准优化内容。
  • [ARC215B] Stolen Necklace 题解
  • 2026最新墙板企业top10推荐!环保/家装/外贸/工程/商业场景优质墙板厂家权威榜单发布 - 品牌推荐2026
  • 论文写作效率翻倍:7个AI网站实用功能全解析。
  • 2026最新WPC格栅厂家top10推荐!环保家装/工程/商业/外贸优质公司权威榜单发布 - 品牌推荐2026
  • 高效论文写作必备:7个AI工具盘点,助你快速完成学术任务。
  • 学术党必备!7款AI工具助你高效撰写毕业论文。
  • vlc录制rtsp流
  • Flink与AnalyticDB for PostgreSQL实时分析