1. 项目概述与背景
在现代Web应用开发中,认证与授权是保障系统安全的核心机制。传统基于Session的认证方式存在服务器内存压力大、跨域限制等问题,而JWT(JSON Web Token)作为一种轻量级的认证方案,通过自包含的Token实现无状态认证,特别适合分布式系统和前后端分离架构。
本项目基于Spring Boot 2.3.2和Spring Security 5.3.2,实现了完整的JWT认证与权限控制方案。核心功能包括:
- 基于JJWT库的Token生成与验证
- Spring Security的深度定制化配置
- 细粒度的权限控制(角色+权限点)
- 完善的异常处理机制
这套方案已在生产环境稳定运行,支持日均10万+的认证请求。下面将详细拆解实现过程中的技术细节和实战经验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与依赖配置
2.1 Maven依赖选型
选择依赖版本时需要考虑Spring Boot的兼容性。本项目使用Spring Boot 2.3.2对应的Spring Security 5.3.2版本:
xml复制<properties>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
</properties>
<dependencies>
<!-- Spring基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT实现库 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
版本选择经验:
- JJWT 0.9.x版本API稳定且兼容JDK8
- 避免使用过新的Spring Security版本,防止与Spring Boot版本冲突
- 生产环境建议锁定所有依赖版本
2.2 密码加密策略
Spring Security提供了多种PasswordEncoder实现,我们选择BCrypt算法:
java复制@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
BCrypt的优势:
- 内置随机盐值,相同密码每次加密结果不同
- 自适应计算成本,可通过参数调整加密强度
- 专门为密码存储设计,抗彩虹表攻击
安全建议:
- 不要使用MD5/SHA-1等传统哈希算法
- 加密强度建议设置为10-12(默认10)
- 前端传输也应加密(如RSA+BCrypt组合)
3. JWT核心实现
3.1 Token生成与解析
JwtUtil类封装了JWT的核心操作:
java复制public class JwtUtil {
private static final Long EXPIRE_TIME = 2 * 60 * 60 * 1000L; // 2小时
private static final String SECRET_KEY = "qwertyuioplkjhgfdsazxcvbnmjkiuysx"; // 32位密钥
public static String generateToken(String userName, Long userNo) {
Map<String, Object> claims = new HashMap<>();
claims.put("userName", userName);
claims.put("userNo", userNo);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Claims parseToken(S
