JWT 认证和token创建的实现(Spring Security)
前沿
一、JWT 基础概念
1.1 什么是 JWT
JWT(JSON Web Token)是一种无状态的认证方案,服务端不需要存储Session,所有信息都编码在 Token 中。


1 Maven 依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.5.2</version>
</dependency>
1.2 application.yml 配置
spring: datasource: url: jdbc:mysql://localhost:3306/mental_health_assistant?useSSL=false username: root password: 123456 server: port: 1236 # JWT 配置 jwt: secret: MySecretKeyForJWT2025!@#$%^&*()_+SecureKeyHere # 密钥 expiration: 86400000 # 24 小时(毫秒) refresh-expiration: 604800000 # 7 天(毫秒) header: Authorization # token 头部名称 token-prefix: "Bearer " # token 前缀
二、JwtConfig —— 配置映射类
作用:把 application.yml 里的 jwt.* 配置,映射成一个 Java Bean,方便注入使用。
package org.example.aispingboot.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data // Lombok 自动生成 getter/setter @Component // 交给 Spring 管理 @ConfigurationProperties(prefix = "jwt") // 绑定 yml 中 jwt 前缀的配置 public class JwtConfig { private String secret; // 密钥 private long expiration; // 过期时间 private long refreshExpiration; // 刷新时间 private String header; // 请求头名 private String tokenPrefix; // token 前缀 }
⚠️ 注意:@Data + @ConfigurationProperties 时必须保证 yml 中的 refresh-expiration(kebab-case)能正确映射到 refreshExpiration(camelCase),Spring Boot 默认支持这种松散绑定,所以没问题。
三、JwtUtil —— 工具类(重点)
3.1 为什么用 ApplicationContextAware?
工具类通常是 静态方法(static),但 Spring 中的 Bean(比如 JwtConfig)需要通过容器注入。静态方法无法直接用 @Autowired。
解决方案:让工具类实现 ApplicationContextAware,在 Spring 启动时拿到 ApplicationContext,之后就可以通过它手动取出 Bean。
package org.example.aispingboot.util; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import org.example.aispingboot.config.JwtConfig; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; // Spring 启动时自动回调,注入上下文 @Override public void setApplicationContext(ApplicationContext applicationContext) { JwtUtil.applicationContext = applicationContext; } // 从 Spring 容器中获取 JwtConfig private static JwtConfig getJwtConfig() { return applicationContext.getBean(JwtConfig.class); } /** * 生成 Token */ public static String generateToken(String userId, String username, Integer roleType) { try { JwtConfig jwtConfig = getJwtConfig(); // 签名算法(HMAC256 + 密钥) Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecret()); // 过期时间点 = 当前时间 + 配置的过期毫秒数 Date expiration = new Date(System.currentTimeMillis() + jwtConfig.getExpiration()); String token = JWT.create() .withClaim("userId", userId) .withClaim("username", username) .withClaim("roleType", roleType) .withExpiresAt(expiration) // 过期时间 .withIssuedAt(new Date()) // 签发时间 .withIssuer("mental-health-assistant") // 签发者 .sign(algorithm); return token; } catch (Exception e) { throw new RuntimeException("Token 生成失败", e); } } /** * 校验 Token 并返回解码结果 */ public static DecodedJWT verifyToken(String token) { JwtConfig jwtConfig = getJwtConfig(); Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecret()); JWTVerifier verifier = JWT.require(algorithm) .withIssuer("mental-health-assistant") .build(); return verifier.verify(token); // 无效会抛异常 } /** * 获取 Token 中的用户ID */ public static String getUserId(String token) { return verifyToken(token).getClaim("userId").asString(); } /** * 获取用户名 */ public static String getUsername(String token) { return verifyToken(token).getClaim("username").asString(); } /** * 获取角色 */ public static Integer getRoleType(String token) { return verifyToken(token).getClaim("roleType").asInt(); } /** * 判断是否过期 */ public static boolean isExpired(String token) { try { return verifyToken(token).getExpiresAt().before(new Date()); } catch (Exception e) { return true; } } }

四、登录接口(生成 Token)
@RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private UserService userService; @PostMapping("/login") public Result<LoginResponseDTO> login(@RequestBody LoginDTO dto) { // 1. 校验用户名密码 User user = userService.findByUsername(dto.getUsername()); if (user == null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } // 2. 生成 token String token = JwtUtil.generateToken( String.valueOf(user.getId()), user.getUsername(), user.getRoleType() ); // 3. 返回 DTO LoginResponseDTO resp = new LoginResponseDTO(); resp.setToken(token); resp.setTokenPrefix("Bearer "); // 或从 JwtConfig 读取 return Result.success(resp); } }
Spring Security(安全框架) 的核心配置类
Spring Boot 项目中用于配置 Spring Security(安全框架) 的核心配置类。
基于 Spring Security 6.x(通常对应 Spring Boot 3.x)的写法。它的主要作用是定义整个系统的安全策略,比如:哪些接口需要登录才能访问、哪些接口可以匿名访问、如何处理登录和登出等。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.config.Customizer; @Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // 1. 关闭 CSRF 防护(如果是前后端分离项目,通常使用 JWT,不需要 CSRF) .csrf(csrf -> csrf.disable()) // 2. 配置请求授权规则 .authorizeHttpRequests(auth -> auth .requestMatchers("/api/travel/hello").permitAll() // 允许 /hello 接口匿名访问 .requestMatchers("/api/user/login").permitAll() // 允许登录接口匿名访问 .anyRequest().authenticated() // 其他所有请求都需要登录认证 ) // 3. 配置 Session 管理(无状态,适合前后端分离) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) // 4. 开启 HTTP Basic 认证(可选,通常前后端分离用 JWT 替代) .httpBasic(Customizer.withDefaults()); return http.build(); // 构建并返回过滤器链 } }
-
@Configuration:Spring 核心注解。告诉 Spring 这是一个配置类,里面可能会包含一个或多个@Bean方法。Spring 启动时会加载这个类。 -
@EnableWebSecurity:Spring Security 核心注解。-
它的作用是开启 Spring Security 的 Web 安全支持。
-
加上它之后,Spring Boot 会自动配置一套默认的安全过滤器链(Filter Chain)。如果我们不自定义这个类,Spring Security 默认会拦截所有请求,并生成一个随机密码,要求所有请求都必须经过 HTTP Basic 认证。
-
-
@EnableMethodSecurity:方法级别安全注解。-
它的作用是开启方法级别的权限控制。
-
加上它之后,你可以在 Service 层或 Controller 层的方法上使用
@PreAuthorize("hasRole('ADMIN')")、@PostAuthorize等注解,来实现细粒度的权限控制(比如:只有管理员才能删除用户)。
-
② 核心 Bean:SecurityFilterChain
-
@Bean:告诉 Spring,这个方法返回的对象需要被注册到 Spring 容器中。 -
SecurityFilterChain:这是 Spring Security 6.x 中最核心的 Bean。它代表了一条安全过滤器链。
Spring Security优化与注意事项(避坑指南)
-
不要返回 null:截图中的
return null;是占位符。实际项目中,必须返回http.build(),否则启动会报错。 -
CSRF 问题:如果是前后端分离项目(Vue/React + Spring Boot),通常需要关闭 CSRF(
.csrf(csrf -> csrf.disable())),否则 POST 请求会报 403 错误。如果是传统的 Thymeleaf 服务端渲染项目,则应该保留 CSRF。 -
跨域问题(CORS):如果前端和后端不在同一个域名/端口,还需要在
SecurityFilterChain中配置 CORS,否则浏览器会拦截请求。可以通过.cors(Customizer.withDefaults())开启,并配合一个CorsConfigurationSourceBean 来配置。 -
方法安全:
@EnableMethodSecurity开启后,可以在 Service 方法上加@PreAuthorize("hasAuthority('user:delete')")这样的注解,实现非常灵活的权限控制。这是 Spring Security 非常强大的功能。
六、整体调用流程
登录: Controller.login() │ ▼ JwtUtil.generateToken(userId, username, roleType) │ ├─ getJwtConfig() ← 从 Spring 容器取配置 ├─ Algorithm.HMAC256(secret) ├─ JWT.create().withClaim(...).sign(algorithm) ▼ 返回 "xxx.yyy.zzz" 给前端 请求: Header: Authorization: Bearer xxx.yyy.zzz │ ▼ JwtInterceptor.preHandle() │ ├─ 取 Header,去掉 "Bearer " ├─ JwtUtil.verifyToken(token) ├─ 解析出 userId/username/roleType ├─ UserContext.set(...) ▼ Controller 方法里用 UserContext.getUserId() 即可拿到当前登录用户
这是单个token 的写法

浙公网安备 33010602011771号