Spring Boot整合JWT 实现双Token机制

server/2025/3/19 16:10:14/

目录

  1. JWT核心概念解析
  2. Spring Boot整合步骤
    • 2.1 基础环境搭建
    • 2.2 Token生成与解析
    • 2.3 拦截器实现
  3. 企业级增强方案
    • 3.1 双Token刷新机制
    • 3.2 安全防护策略
  4. 常见问题与解决方案

1. JWT核心概念解析

1.1 Token的三重使命

  • 身份凭证:替代Session实现无状态认证
  • 信息载体:存储用户基础信息(如userid、roles)
  • 安全屏障:数字签名防止数据篡改

1.2 JWT结构示例

Header
{"alg": "HS256","typ": "JWT"
}Payload
{"sub": "123456","name": "John","iat": 1516239022
}Signature
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

2. Spring Boot整合步骤

2.1 基础环境搭建

依赖配置

<!-- pom.xml -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

配置文件

# application.yml
jwt:secret: your-256-bit-encryption-keyexpiration: 7200 # 2小时header: Authorization

2.2 Token生成与解析

工具类实现

java">@Component
public class JwtUtils {// 注入配置参数@Value("${jwt.secret}")private String secret;// 生成Tokenpublic String generateToken(UserDetails user) {return Jwts.builder().setSubject(user.getUsername()).claim("roles", user.getAuthorities()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)).signWith(SignatureAlgorithm.HS256, secret).compact();}// 解析Tokenpublic Claims parseToken(String token) {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}
}

2.3 拦截器实现

认证拦截器

java">public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 检查白名单路径if (isWhiteList(request.getRequestURI())) {return true;}// 2. 获取并验证TokenString token = request.getHeader(jwtProperties.getHeader());if (!jwtUtils.validateToken(token)) {throw new UnauthorizedException("无效的访问凭证");}// 3. 注入用户信息Claims claims = jwtUtils.parseToken(token);request.setAttribute("userId", claims.getSubject());return true;}private boolean isWhiteList(String uri) {return Arrays.asList("/api/login", "/api/refresh").contains(uri);}
}

注册拦截器

java">@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor()).addPathPatterns("/api/**").excludePathPatterns("/api/auth/**");}
}

3. 企业级增强方案

3.1 双Token刷新机制

流程图解

Client Server 登录请求(账号+密码) AccessToken(2h)+RefreshToken(7d) 携带AccessToken请求 返回业务数据 loop [正常访问] 携带RefreshToken请求刷新 返回新AccessToken 重新登录 alt [AccessToken过期] [RefreshToken过期] Client Server

刷新接口实现

java">@PostMapping("/refresh")
public Result refreshToken(@RequestParam String refreshToken) {if (redisTemplate.hasKey(refreshToken)) {String username = redisTemplate.opsForValue().get(refreshToken);UserDetails user = userService.loadUserByUsername(username);String newAccessToken = jwtUtils.generateToken(user);return Result.ok().data("accessToken", newAccessToken);}throw new BusinessException(600, "刷新令牌已失效");
}

3.2 安全防护策略

五层防御体系

  1. 传输加密:强制使用HTTPS
  2. 存储安全:前端使用HttpOnly Cookie
  3. 自动续期:Access Token有效期≤2小时
  4. 黑名单:登出Token立即失效
  5. 限流控制:接口请求频率限制

黑名单实现

java">@Component
public class TokenBlacklist {@Autowiredprivate RedisTemplate<String, String> redisTemplate;// Token加入黑名单(有效期剩余时间)public void addToBlacklist(String token) {Date expiration = jwtUtils.getExpirationFromToken(token);long ttl = expiration.getTime() - System.currentTimeMillis();redisTemplate.opsForValue().set(token, "invalid", ttl, TimeUnit.MILLISECONDS);}
}

4. 常见问题与解决方案

问题现象根本原因解决方案
签名验证失败密钥不一致或Token被篡改统一密钥管理(配置中心)
Token突然失效服务器时间不同步部署NTP时间同步服务
高并发下认证超时RSA算法性能瓶颈切换为HS256算法
无法强制下线已登录用户无状态特性导致结合Redis维护短期Token黑名单

总结:JWT为现代分布式系统提供了优雅的认证解决方案,但实际落地时需综合考虑安全、性能、用户体验等多维度因素。建议结合具体业务场景选择合适的Token管理策略。


http://www.ppmy.cn/server/176281.html

相关文章

PHP PDO 教程:深入理解与高效使用

PHP PDO 教程:深入理解与高效使用 引言 PHP PDO(PHP Data Objects)扩展是PHP中用于访问数据库的一个抽象层。PDO提供了一个数据访问抽象层,可以让你使用相同的函数和方法来访问多种数据库,从而不必为每种数据库编写特定的代码。本文将深入探讨PHP PDO的使用,包括其基本…

python字符级差异分析并生成 Word 报告

import difflib from docx import Document from docx.shared import RGBColordef analyze_char_differences(text_a, text_b):"""分析两个文本的字符级差异:param text_a: 第一个文本:param text_b: 第二个文本"""matcher difflib.SequenceMat…

堆排序:力扣215.数组中的第K个大元素

一、问题描述 在一个整数数组 nums 中&#xff0c;需要找出第 k 个最大的元素。这里要注意&#xff0c;我们要找的是数组排序后的第 k 个最大元素&#xff0c;而不是第 k 个不同的元素。例如&#xff0c;对于数组 [3,2,1,5,6,4]&#xff0c;当 k 2 时&#xff0c;第 2 个最大…

m4i.22xx-x8系列-PCIe总线直流耦合5G采集卡

m4i.22xx-x8系列-PCIe总线直流耦合采集卡 四通道1.25 GS/s&#xff1b;两通道2.5GS/s&#xff1b;单通道5GS/s&#xff1b;500 MHz或1.5GHz带宽&#xff1b;标准4 GSample板载内存 概述&#xff1a; m4i.22xx-x8系列是针对高速数据采集而设计&#xff0c;包含9个型号。模块的…

Cursor AI IDE

前言 Cursor IDE 描述-->AI代码编辑器、AI IDE、集成开发环境 Cursor IDE: 这是一个集成了 AI 功能的代码编辑器&#xff0c;基于 Visual Studio Code (VSCode)&#xff0c;即带有内置 AI 的 VSCode。它利用 AI 技术来增强编程体验&#xff0c;提供智能代码补全、错误检测、…

某快餐店用户市场数据挖掘与可视化

1、必要库的载入 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns2、加载并清洗数据 # 2.1 加载数据 df pd.read_csv(/home/mw/input/survey6263/mcdonalds.csv)# 2.2 数据清洗 # 2.2.1 检查缺失值 print(缺失值情况&#xff1a;) print(df.isn…

C++特性——智能指针

为什么需要智能指针 对于定义的局部变量&#xff0c;当作用域结束之后&#xff0c;就会自动回收&#xff0c;这没有什么问题。 当时用new delete的时候&#xff0c;就是动态分配对象的时候&#xff0c;如果new了一个变量&#xff0c;但却没有delete&#xff0c;这会造成内存泄…

【canvas】一键自动布局:如何让流程图节点自动找到最佳位置

一键自动布局&#xff1a;如何让流程图节点自动找到最佳位置 引言 在流程图、拓扑图和系统架构图设计中&#xff0c;节点布局往往是最令人头疼的问题。如果手动调整每个节点位置&#xff0c;不仅耗时费力&#xff0c;还难以保证美观性和一致性。本文将深入解析如何实现自动布…