使用SpringBoot3.x+Spring Security对JWT进行鉴权
·
Spring Boot 3.x + Spring Security 对 JWT 进行鉴权
1. 引言
JSON Web Token(JWT)作为一种轻量级的认证机制,在现代Web应用中得到了广泛应用。它具有无状态、可扩展、跨平台等优点,特别适合前后端分离架构和微服务场景。
本文将详细介绍如何在Spring Boot 3.x项目中,使用Spring Security对JWT进行鉴权,包括JWT的生成、验证、刷新等核心功能的实现。
2. 技术栈
- Spring Boot 3.5.x
- Spring Security 6.5.x
- Nimbus JOSE + JWT 10.x
3. JWT原理与结构
3.1 JWT的组成
JWT由三部分组成,用点(.)分隔:
- Header:包含令牌类型和签名算法
- Payload:包含声明(claims),如用户ID、角色等
- Signature:使用密钥对前两部分进行签名
Header.Payload.Signature
3.2 JWT的工作流程
┌──────────────┐ ┌──────────────┐
│ 客户端 │ 1. 登录请求 │ 认证服务器 │
│ │ ─────────────────────> │ │
│ │ │ │
└──────────────┘ └──────────────┘
│
│ 2. 验证用户凭证
│
▼
┌──────────────┐ ┌──────────────┐
│ 客户端 │ 3. 返回JWT Token │ 认证服务器 │
│ │ <───────────────────── │ │
│ │ │ │
└──────────────┘ └──────────────┘
│
│ 4. 携带Token请求受保护资源
▼
┌──────────────┐ ┌──────────────┐
│ 客户端 │ 5. 请求携带JWT Token │ 资源服务器 │
│ │ ─────────────────────> │ │
│ │ │ │
└──────────────┘ └──────────────┘
│
│ 6. 验证Token
│
▼
┌──────────────┐ ┌──────────────┐
│ 客户端 │ 7. 返回资源 │ 资源服务器 │
│ │ <───────────────────── │ │
│ │ │ │
└──────────────┘ └──────────────┘
4. 项目初始化
4.1 创建Spring Boot项目
使用Spring Boot Initializr创建项目,添加以下依赖:
- Spring Web
- Spring Security
- Lombok
4.2 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.8</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>xxx-security-authentication-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>${project.artifactId}</name>
<properties>
<java.version>21</java.version>
<nimbus-jose-jwt.version>10.0.2</nimbus-jose-jwt.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose-jwt.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5. 核心功能实现
5.1 配置文件
在application.yml中添加配置:
server:
port: 8080
spring:
application:
name: xxx-security-authentication-client
logging:
level:
org.springframework.security: DEBUG
5.2 JWT工具类
创建JwtUtil.java:
public class JwtUtil {
private final JWSVerifier verifier;
private final PublicKey publicKey;
public JwtUtil(PublicKey publicKey) {
this.verifier = createVerifier(publicKey);
this.publicKey = publicKey;
}
private JWSVerifier createVerifier(PublicKey publicKey) {
return new RSASSAVerifier((RSAPublicKey) publicKey);
}
public boolean validateToken(String token) throws JOSEException, ParseException {
try {
SignedJWT signedJWT = SignedJWT.parse(token);
boolean verified = signedJWT.verify(verifier);
boolean notExpired = signedJWT.getJWTClaimsSet().getExpirationTime().after(new Date());
return verified && notExpired;
} catch (JOSEException | ParseException e) {
throw e;
}
}
public JWTClaimsSet getClaims(String token) throws ParseException {
SignedJWT signedJWT = SignedJWT.parse(token);
return signedJWT.getJWTClaimsSet();
}
public String getSubject(String token) throws ParseException {
return getClaims(token).getSubject();
}
public List<String> getAuthorities(String token) throws ParseException {
return (List<String>) getClaims(token).getClaim("authorities");
}
public String getUsername(String token) throws ParseException {
return getClaims(token).getStringClaim("username");
}
public String getUserId(String token) throws ParseException {
return getClaims(token).getStringClaim("userId");
}
}
5.3 会话用户实体类
创建AuthUser.java:
@Data
public class AuthUser implements UserDetails {
private String userId;
private String username;
private String password;
private Collection<GrantedAuthority> authorities = new HashSet<>();
private boolean enabled = true;
private boolean accountNonExpired = true;
private boolean accountNonLocked = true;
private boolean credentialsNonExpired = true;
}
5.4 创建认证Token转换器AuthenticationConverter
创建AuthTokenAuthenticationConverter.java:
public class AuthTokenAuthenticationConverter implements AuthenticationConverter {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_PREFIX = "Bearer ";
@Override
public Authentication convert(HttpServletRequest request) {
String token = extractToken(request);
if(!StringUtils.hasText(token)) {
return null;
}
return new AuthTokenAuthenticationToken(token);
}
private String extractToken(HttpServletRequest request) {
String token = request.getHeader(AUTHORIZATION_HEADER);
if(!StringUtils.hasText(token)) {
return null;
}
if (token.startsWith(BEARER_PREFIX)) {
token = token.substring(BEARER_PREFIX.length());
}
return token;
}
}
5.5 创建认证Token认证提供者AuthenticationProvider
创建AuthTokenAuthenticationProvider.java:
@Slf4j
public final class AuthTokenAuthenticationProvider implements AuthenticationProvider {
private final JwtUtil jwtUtil;
public AuthTokenAuthenticationProvider(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthTokenAuthenticationToken jwtAuthenticationToken = (AuthTokenAuthenticationToken) authentication;
Object credentials = jwtAuthenticationToken.getCredentials();
try {
String token = (String) credentials;
if (jwtUtil.validateToken(token)) {
JWTClaimsSet claimsSet = jwtUtil.getClaims(token);
String username = claimsSet.getStringClaim("username");
String userId = claimsSet.getStringClaim("userId");
List<String> roles = (List<String>) claimsSet.getClaim("authorities");
List<GrantedAuthority> authorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
AuthUser authUser = new AuthUser();
authUser.setUsername(username);
authUser.setUserId(userId);
authUser.setAuthorities(authorities);
return new AuthTokenAuthenticationToken(authUser, credentials, authorities);
}else {
throw new BadCredentialsException("Invalid Jwt Token.");
}
} catch (AuthenticationException e) {
throw e;
} catch (Exception e) {
throw new BadCredentialsException("Jwt Token Exception.");
}
}
@Override
public boolean supports(Class<?> authentication) {
return AuthTokenAuthenticationToken.class.isAssignableFrom(authentication);
}
}
5.6 JWT认证过滤器
配置认证过滤器AuthenticationFilter:
public AuthenticationFilter authClientAuthenticationFilter() {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager, new AuthTokenAuthenticationConverter());
authenticationFilter.setSuccessHandler(new AuthClientAuthenticationSuccessHandler());
return authenticationFilter;
}
6. 参考资源
完整代码请参考项目仓库:mwc-security-authentication-client
演示地址:mwcs
更多推荐




所有评论(0)