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

Spring Boot + JWT 实现无状态认证

1. JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地将信息作为 JSON 对象传输。JWT 是目前最流行的跨域认证解决方案,特别适合前后端分离的架构。

1.1 JWT 的结构

JWT 由三部分组成,用点号(.)分隔:

xxxxx.yyyyy.zzzzz │ │ │ │ │ └── Signature(签名) │ └───────── Payload(负载) └──────────────── Header(头部)

Header:包含令牌类型和签名算法

{"alg":"HS256","typ":"JWT"}

Payload:包含声明(claims),如用户信息、过期时间等

{"username":"zhangsan","iat":1516239022,"exp":1516242622}

Signature:使用密钥对前两部分进行签名,确保令牌不被篡改

1.2 JWT 的优势

优势说明
无状态服务器不需要保存会话信息,令牌自包含用户信息
跨域支持天然支持 CORS,适合前后端分离架构
扩展性强令牌中可以携带自定义信息
安全性高使用签名防止篡改,支持过期时间

2. 项目架构

┌─────────────────────────────────────────────────────────────┐ │ 客户端 (Vue 3) │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ 登录请求 │ │ 存储 Token │ │ 请求携带 Token │ │ │ └──────────────┘ └──────────────┘ └────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ │ HTTP/REST ▼ ┌─────────────────────────────────────────────────────────────┐ │ 服务端 (Spring Boot) │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ 生成 Token │ │ 验证 Token │ │ 授权访问资源 │ │ │ │ (JwtUtil) │ │ (JwtFilter) │ │ (SecurityConfig) │ │ │ └──────────────┘ └──────────────┘ └────────────────────┘ │ └─────────────────────────────────────────────────────────────┘

3. 核心组件实现

3.1 JwtUtil - JWT 工具类

负责生成、解析和验证 JWT 令牌。

@ComponentpublicclassJwtUtil{// 使用 HS256 算法生成密钥privatefinalKeykey=Keys.secretKeyFor(SignatureAlgorithm.HS256);@AutowiredprivateAppConfigappConfig;/** * 生成 JWT 令牌 * @param username 用户名 * @return JWT 令牌字符串 */publicStringgenerateToken(Stringusername){Map<String,Object>claims=newHashMap<>();claims.put("username",username);returnJwts.builder().setClaims(claims)// 设置自定义声明.setSubject(username)// 设置主题(用户名).setIssuedAt(newDate())// 设置签发时间.setExpiration(newDate(System.currentTimeMillis()+getExpirationTime()))// 设置过期时间.signWith(key)// 使用密钥签名.compact();// 生成令牌}/** * 从令牌中提取用户名 * @param token JWT 令牌 * @return 用户名 */publicStringextractUsername(Stringtoken){Claimsclaims=Jwts.parserBuilder().setSigningKey(key)// 设置验证密钥.build().parseClaimsJws(token)// 解析令牌.getBody();// 获取声明体returnclaims.getSubject();}/** * 验证令牌是否有效 * @param token JWT 令牌 * @return 是否有效 */publicbooleanvalidateToken(Stringtoken){try{Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);returntrue;// 解析成功,令牌有效}catch(Exceptione){returnfalse;// 解析失败,令牌无效或过期}}}

关键点说明

  • 使用Keys.secretKeyFor(SignatureAlgorithm.HS256)生成安全的密钥
  • setExpiration()设置令牌过期时间,增强安全性
  • validateToken()捕获所有异常,确保令牌验证的健壮性

3.2 JwtFilter - JWT 过滤器

拦截请求,验证 JWT 令牌的有效性。

@ComponentpublicclassJwtFilterextendsOncePerRequestFilter{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(JwtFilter.class);@AutowiredprivateJwtUtiljwtUtil;@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainchain)throwsServletException,IOException{// 从请求头中获取令牌StringauthorizationHeader=request.getHeader("Authorization");Stringtoken=null;Stringusername=null;// 检查授权头,提取 Bearer Tokenif(authorizationHeader!=null&&authorizationHeader.startsWith("Bearer ")){token=authorizationHeader.substring(7);// 去掉 "Bearer " 前缀try{username=jwtUtil.extractUsername(token);}catch(Exceptione){logger.error("Error extracting username from token: {}",e.getMessage());}}// 验证令牌并设置认证上下文if(username!=null&&SecurityContextHolder.getContext().getAuthentication()==null){if(jwtUtil.validateToken(token)){// 创建认证令牌UsernamePasswordAuthenticationTokenauthToken=newUsernamePasswordAuthenticationToken(username,null,null);authToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(request));// 设置安全上下文SecurityContextHolder.getContext().setAuthentication(authToken);}}chain.doFilter(request,response);}}

关键点说明

  • 继承OncePerRequestFilter确保每个请求只过滤一次
  • Authorization头中提取Bearer Token
  • 使用SecurityContextHolder设置认证信息,供后续业务使用

3.3 SecurityConfig - 安全配置

配置 Spring Security,定义安全规则和认证方式。

@Configuration@EnableWebSecuritypublicclassSecurityConfig{@AutowiredprivateJwtFilterjwtFilter;@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurityhttp)throwsException{http// 配置 CORS.cors(cors->cors.configurationSource(corsConfigurationSource()))// 禁用 CSRF(前后端分离不需要).csrf(csrf->csrf.disable())// 配置请求授权.authorizeHttpRequests(authorize->authorize// 允许匿名访问的路径.requestMatchers("/api/v1/users/register","/api/v1/users/login","/api/v1/users/security-question","/api/v1/users/reset-password","/api/v1/captcha/**").permitAll()// AI 相关接口允许匿名访问.requestMatchers("/api/v1/ai-analysis/**","/api/v1/ai-assistant/**").permitAll()// Swagger 文档允许访问.requestMatchers("/swagger-ui.html","/swagger-ui/**","/v3/api-docs/**").permitAll()// 其他请求需要认证.anyRequest().authenticated())// 添加 JWT 过滤器.addFilterBefore(jwtFilter,UsernamePasswordAuthenticationFilter.class);returnhttp.build();}@BeanpublicCorsConfigurationSourcecorsConfigurationSource(){CorsConfigurationconfiguration=newCorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));configuration.setAllowedHeaders(Arrays.asList("Content-Type","Authorization"));configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**",configuration);returnsource;}}

关键点说明

  • 禁用 CSRF 保护(前后端分离架构不需要)
  • 配置白名单路径,允许匿名访问登录、注册等接口
  • 使用addFilterBefore()将 JWT 过滤器添加到 Spring Security 过滤器链中

4. 认证流程

4.1 登录流程

1. 用户发送登录请求 POST /api/v1/users/login { "username": "zhangsan", "password": "password123", "captcha": "a3f8" } 2. UserController 接收请求,验证用户名密码 3. 验证成功,调用 JwtUtil.generateToken(username) 生成 JWT 4. 返回响应,包含 JWT 令牌 { "success": true, "data": { "token": "eyJhbGciOiJIUzI1NiIs...", "username": "zhangsan" } } 5. 前端存储 Token(localStorage 或 sessionStorage)

4.2 请求认证流程

1. 用户访问受保护资源,请求头携带 Token Authorization: Bearer eyJhbGciOiJIUzI1NiIs... 2. JwtFilter 拦截请求,从 Header 提取 Token 3. 验证 Token 有效性(签名、过期时间) 4. 验证通过,设置 SecurityContext 认证信息 5. 请求到达 Controller,执行业务逻辑 6. 返回业务数据

5. 代码示例

5.1 登录接口实现

@RestController@RequestMapping("/api/v1/users")publicclassUserController{@AutowiredprivateUserServiceuserService;@AutowiredprivateJwtUtiljwtUtil;privateBCryptPasswordEncoderpasswordEncoder=newBCryptPasswordEncoder();@PostMapping("/login")publicUserResponselogin(@Valid@RequestBodyUserLoginRequestrequest,HttpServletRequesthttpRequest){// 1. 验证验证码Stringcaptcha=request.getCaptcha();if(!validateCaptcha(captcha,httpRequest.getSession())){thrownewRuntimeException("验证码错误");}// 2. 验证用户名密码Useruser=userService.login(request.getUsername(),request.getPassword());// 3. 生成 JWT TokenStringtoken=jwtUtil.generateToken(user.getUsername());// 4. 构建响应UserResponseresponse=newUserResponse();response.setToken(token);response.setUsername(user.getUsername());response.setEmail(user.getEmail());// ... 其他用户信息returnresponse;}}

5.2 受保护接口示例

@RestController@RequestMapping("/api/v1/food-records")publicclassFoodRecordController{@GetMappingpublicList<FoodRecord>getRecords(@RequestParamStringusername){// 由于 JwtFilter 已经验证过 Token// 这里可以直接获取当前认证用户Authenticationauth=SecurityContextHolder.getContext().getAuthentication();StringcurrentUser=auth.getName();// 验证只能查询自己的记录if(!currentUser.equals(username)){thrownewRuntimeException("无权访问其他用户的数据");}returnfoodRecordService.findByUsername(username);}}

6. 前端集成

6.1 登录后存储 Token

// 登录成功,保存 Tokenconstlogin=async(credentials)=>{constresponse=awaitapi.post('/api/v1/users/login',credentials);const{token,username}=response.data;// 存储到 localStoragelocalStorage.setItem('token',token);localStorage.setItem('username',username);returnresponse.data;};

6.2 请求拦截器添加 Token

// Axios 请求拦截器api.interceptors.request.use(config=>{consttoken=localStorage.getItem('token');if(token){config.headers.Authorization=`Bearer${token}`;}returnconfig;},error=>Promise.reject(error));

6.3 响应拦截器处理 401

// Axios 响应拦截器api.interceptors.response.use(response=>response.data,error=>{if(error.response?.status===401){// Token 过期或无效,清除存储并跳转登录页localStorage.removeItem('token');window.location.href='/';}returnPromise.reject(error);});

7. 安全注意事项

7.1 密钥管理

  • 生产环境:密钥应存储在环境变量或配置中心,不要硬编码
  • 密钥轮换:定期更换密钥,旧令牌会自然失效
  • 密钥强度:使用足够强度的密钥(HS256 自动生成)

7.2 令牌安全

风险防护措施
XSS 攻击使用 httpOnly Cookie 存储令牌,或做好 XSS 防护
CSRF 攻击前后端分离天然免疫 CSRF,但仍需验证 Origin
令牌泄露设置合理的过期时间(通常 1-24 小时)
重放攻击结合时间戳和随机数,或使用短期令牌

7.3 传输安全

  • 生产环境必须使用 HTTPS
  • 敏感接口增加额外的验证(如密码修改需要旧密码)
http://www.jsqmd.com/news/798308/

相关文章:

  • VideoDownloadHelper:3步实现全网视频下载的智能工具
  • Matlab实战:基于EGM2008模型与球谐函数解析全球重力梯度场
  • 学习进度4/10
  • 深度解析:如何构建广谱注入Chromium/V8的通用修改器
  • YOLOv11 改进 - 注意力机制 ACmix自注意力与卷积混合模型:轻量级设计融合双机制优势,实现高效特征提取与推理加速
  • 别再只用Speedtest了!用群晖Docker部署Homebox,打造你的专属内网万兆测速站
  • 健康管理PPT风格描述提示词
  • Java面试跳槽需要提前准备什么内容?
  • 计算机毕业设计:Python医疗文本挖掘与可视化决策平台 Flask框架 随机森林 机器学习 疾病数据 智慧医疗 深度学习(建议收藏)✅
  • Sonos家庭影院音频设置指南:微调设置,提升音质与沉浸感!
  • 07 二叉树的最小深度
  • FanControl深度解析:如何为Windows打造智能静音散热系统
  • 5月重磅|2026苏州GEO优化公司TOP5实力盘点+GEO攻略+GEO优化 - 一网推GEO招财兔
  • 深度解析React核心机制:从组件到虚拟DOM的全面指南
  • H3C WA5320云AP瘦转胖实战:从BootWare升级到固件刷写的完整避坑指南
  • 梯度下降变体:SGD、Adam、RMSProp 对比实验
  • 数字的长征:从蒸汽机到智能体——可计算化革命的底层演进脉络
  • 【AI】FastFolders.exe v5.14.2 许可分析
  • 【实战指南】PLSQL Developer 13 从零配置到高效开发:安装、注册与核心功能详解
  • YOLOv11 改进 - 注意力机制 CascadedGroupAttention级联组注意力:动态感受野适配复杂场景,增强小目标特征捕获
  • 复杂SoC PMU管理:Q-Channel协议
  • vnc 7 主机参数设置-不能从客户端复制文本到主机
  • C++学习(26_05_11)
  • RouterOS一线多拨实战:从零配置到负载均衡策略深度解析
  • 2026年4月太阳膜品牌连锁店推荐,可靠的太阳膜连锁店,防雾功能太阳膜,雨天驾驶更安全 - 品牌推荐师
  • 一文搞懂:JWT(JSON Web Token)与Token认证——从结构剖析到签名算法,再到刷新与注销全攻略
  • HX711 24位ADC模块终极指南:从零开始实现高精度称重测量
  • 别再死记硬背参数了!手把手教你用ANSYS Workbench定义自己的永磁体材料库
  • ledger官网购买这三年:从代购主导到直营落地的渠道演变
  • 告别CondaHTTPError:一份保姆级的Conda镜像源管理与故障排查指南(2024版)