2026 年,Spring Security 6 已成为 Java 后端安全的绝对标配。但许多开发者仍在使用过时的
WebSecurityConfigurerAdapter,或对 JWT 的无状态校验、OAuth2 的授权码流程一知半解。本文将从零搭建一个生产级认证中心,涵盖 Spring Security 6 新配置范式、JWT 令牌生成与解析、RBAC 权限控制、OAuth2 三方登录四大模块,代码完整可运行。
一、为什么需要认证中心?
在微服务架构中,每个服务都独立做身份验证是灾难性的。认证中心(Authorization Server) 集中处理登录、颁发令牌,各业务服务通过令牌校验身份,从而实现 SSO(单点登录) 和统一权限管理。
Spring Security 6 与 Spring Boot 3.x 配合,弃用了旧版 WebSecurityConfigurerAdapter,全面拥抱 Lambda DSL + 组件化配置。我们将基于 Spring Boot 3.4.2 + Spring Security 6.4.1 构建。
-1024x89.png)
二、项目初始化与依赖
使用 Spring Initializr 创建项目,依赖如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.2</version>
</parent>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Web 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JWT 操作 (jjwt 0.12.x) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<!-- 数据库与 JPA (用于用户存储) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 密码编码 (BCrypt) -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
</dependencies>
注意:jjwt 0.12.x 的 API 与 0.11.x 有较大变化,本文使用最新的构建器模式。
三、用户存储与认证逻辑
3.1 用户实体与 Repository
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String username;
private String password; // 存储 BCrypt 哈希
private String email;
private String roles; // 如 "ROLE_USER,ROLE_ADMIN"
// getters & setters ...
}
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
3.2 自定义 UserDetailsService
Spring Security 通过 UserDetailsService 加载用户信息。
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
// 将逗号分隔的角色转为 GrantedAuthority 列表
List<GrantedAuthority> authorities = Arrays.stream(user.getRoles().split(","))
.map(role -> new SimpleGrantedAuthority(role.trim()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
authorities
);
}
}
3.3 密码编码器
在 Security 配置中声明 BCryptPasswordEncoder Bean,它使用强度为 10 的哈希,安全性足够。
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
四、JWT 工具类(签发与校验)
jjwt 0.12.x 使用 Jwts.builder() 构建令牌,并支持 CompactJwsBuilder 链式调用。
@Component
public class JwtUtils {
@Value("${jwt.secret}")
private String secret; // 建议从环境变量读取,至少 256 位
@Value("${jwt.expirationMs}")
private int expirationMs;
// 生成 JWT
public String generateToken(String username, List<String> roles) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationMs);
return Jwts.builder()
.subject(username)
.claim("roles", roles)
.issuedAt(now)
.expiration(expiryDate)
.signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)), Jwts.SIG.HS256)
.compact();
}
// 解析 JWT 并获取 Claims
public Claims getClaims(String token) {
return Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.build()
.parseSignedClaims(token)
.getPayload();
}
// 校验令牌是否有效(含过期检查)
public boolean validateToken(String token) {
try {
getClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
// 从令牌中提取用户名
public String getUsername(String token) {
return getClaims(token).getSubject();
}
}
五、Spring Security 6 核心配置(抛弃 WebSecurityConfigurerAdapter)
Spring Security 6 推荐使用 SecurityFilterChain Bean + Lambda DSL。
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // 启用方法级权限注解
public class SecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtUtils jwtUtils;
// 1. 配置密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 2. 配置认证管理器(使用 DaoAuthenticationProvider)
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return http.authenticationProvider(authProvider).getSharedObject(AuthenticationManager.class);
}
// 3. 核心过滤器链:禁用 CSRF、设置无状态 Session、配置 JWT 过滤器
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception {
// JWT 认证过滤器(自定义)
JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter(jwtUtils, userDetailsService);
http
.csrf(csrf -> csrf.disable()) // 无状态 API 无需 CSRF
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login", "/auth/register", "/oauth2/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.authenticationManager(authManager);
return http.build();
}
}
5.1 JwtAuthenticationFilter(核心过滤器)
该过滤器拦截每个请求,从 Authorization Header 提取 JWT 并校验,将用户信息放入 SecurityContext。
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
private final CustomUserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtUtils jwtUtils, CustomUserDetailsService userDetailsService) {
this.jwtUtils = jwtUtils;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = extractJwtFromRequest(request);
if (token != null && jwtUtils.validateToken(token)) {
String username = jwtUtils.getUsername(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
private String extractJwtFromRequest(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
六、登录接口与注册接口
6.1 登录控制器
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticationManager authManager;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 1. 执行认证
Authentication authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
// 2. 获取用户信息
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
// 3. 生成 JWT
String token = jwtUtils.generateToken(userDetails.getUsername(), roles);
return ResponseEntity.ok(new JwtResponse(token));
}
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
return ResponseEntity.badRequest().body("用户名已存在");
}
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setEmail(request.getEmail());
user.setRoles("ROLE_USER"); // 默认角色
userRepository.save(user);
return ResponseEntity.ok("注册成功");
}
}
6.2 请求/响应 DTO
public class LoginRequest {
private String username;
private String password;
// getters/setters
}
public class JwtResponse {
private String token;
private String type = "Bearer";
// constructor, getters
}
七、RBAC 权限控制(方法级注解)
Spring Security 6 通过 @PreAuthorize 实现方法级权限校验,需在配置类上开启 @EnableMethodSecurity。
@RestController
@RequestMapping("/admin")
public class AdminController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/users")
public List<User> listAllUsers() {
// 只有 ADMIN 可访问
return userService.findAll();
}
@PreAuthorize("hasAuthority('SCOPE_write')") // 可用于 OAuth2 scope
@PostMapping("/data")
public String updateData() {
return "Data updated";
}
}
权限表达式支持 hasRole、hasAuthority、hasAnyRole、@ 自定义 Bean 方法调用等。
八、OAuth2 三方登录集成(以 GitHub 为例)
Spring Security 6 内置 OAuth2 客户端支持,只需配置 application.yml。
8.1 依赖(已包含在 spring-boot-starter-security 中)
无需额外依赖,但需添加 spring-security-oauth2-client 自动配置(Spring Boot 已包含)。
8.2 配置 application.yml
spring:
security:
oauth2:
client:
registration:
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
scope: read:user
provider:
github:
authorization-uri: https://github.com/login/oauth/authorize
token-uri: https://github.com/login/oauth/access_token
user-info-uri: https://api.github.com/user
user-name-attribute: id
8.3 自定义 OAuth2 登录成功处理器
登录成功后,我们根据 GitHub 用户信息生成自己的 JWT 令牌,返回给前端。
@Component
public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserRepository userRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
Map<String, Object> attributes = token.getPrincipal().getAttributes();
String githubId = attributes.get("id").toString();
String name = (String) attributes.get("login");
// 查找或创建用户(这里简化)
User user = userRepository.findByUsername(githubId).orElseGet(() -> {
User newUser = new User();
newUser.setUsername(githubId);
newUser.setPassword(UUID.randomUUID().toString()); // 随机密码,不可用
newUser.setRoles("ROLE_USER");
return userRepository.save(newUser);
});
// 生成 JWT 并重定向到前端回调地址
String jwt = jwtUtils.generateToken(user.getUsername(), List.of(user.getRoles().split(",")));
response.sendRedirect("http://localhost:3000/oauth2/redirect?token=" + jwt);
}
}
在 SecurityConfig 的 filterChain 中添加 OAuth2 相关配置:
http
.oauth2Login(oauth2 -> oauth2
.successHandler(oAuth2LoginSuccessHandler)
.loginPage("/oauth2/authorization/github") // 触发 GitHub 登录的端点
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**", "/oauth2/**").permitAll()
.anyRequest().authenticated()
);
之后访问 /oauth2/authorization/github 即跳转到 GitHub 授权页,授权后回调并携带 JWT。
九、测试与验证
启动应用后,可使用 curl 测试:
# 1. 注册用户
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456","email":"test@demo.com"}'
# 2. 登录获取 JWT
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}'
# 返回: {"token":"eyJ...", "type":"Bearer"}
# 3. 访问受保护接口
curl -X GET http://localhost:8080/admin/users \
-H "Authorization: Bearer eyJ..."
若 test 用户只有 ROLE_USER,访问 /admin/users 将返回 403 Forbidden,证明 RBAC 生效。
十、生产环境最佳实践
- JWT Secret 管理:使用环境变量或配置中心,长度至少 256 位(HS256),推荐使用 RSA 非对称加密(更安全,可公钥校验)。
- 令牌刷新:JWT 过期后,可通过
/refresh接口,用 Refresh Token 换取新令牌(本文未实现,但可参考 jjwt 的 Refresh Token 模式)。 - 日志与监控:集成 MDC 记录登录失败、令牌过期等事件,参考本站 《Java 日志最佳实践 2026》(待发布,可先关联 《Java 应用接入 Prometheus + Grafana 全记录》 监控部分)。
- 多因素认证:Spring Security 6 支持集成 TOTP(Google Authenticator),可通过扩展
AuthenticationProvider实现。 - 容器化部署:参考 《Spring Boot 3.4 Docker 镜像最佳实践》 制作镜像,将 Secret 作为容器环境变量注入。
十一、总结
本文完整实现了基于 Spring Security 6 + JWT + OAuth2 的认证中心,涵盖了从用户存储、JWT 工具、无状态过滤器、RBAC 权限到三方登录的全链路。Spring Security 6 的 Lambda 配置更简洁,与 Spring Boot 3.x 深度融合,让安全模块不再是“黑盒”。掌握这套方案,你就能为微服务架构构建坚实的身份与权限基座。
未来可扩展方向:支持多租户、集成 Keycloak 作为外部认证、使用 Spring Authorization Server 构建完整 OAuth2 授权服务器。
📌 系列拓展阅读:
- 《Java 日志最佳实践 2026:从 SLF4J 到 ELK 全链路日志追踪》——日志与安全审计结合
- 《Spring Boot 3.4 Docker 镜像最佳实践(含分层构建)》——容器化部署安全配置
- 《Java 应用接入 Prometheus + Grafana 全记录》——监控认证服务的健康状态
- 《Spring AI 1.0 正式版深度解析》——安全与 AI 结合(如权限校验的 AI 化)
📚 参考文献:
- Spring Security 官方文档. 6.4.1 Reference. https://docs.spring.io/spring-security/reference/index.html
- JJWT GitHub. 0.12.6 API. https://github.com/jwtk/jjwt
- OAuth 2.0 标准. RFC 6749. https://datatracker.ietf.org/doc/html/rfc6749
- Baeldung. Spring Security 6 with JWT. https://www.baeldung.com/spring-security-6-jwt
- Spring Blog. Spring Security 6.0 Migration Guide. https://spring.io/blog/2022/05/12/spring-security-6-0
- 本站. Java 应用接入 Prometheus + Grafana 全记录. https://www.macs.vip/archives/835









