一、Spring Security 6 与 OAuth 2.0 概述
Spring Security 6是Spring Security的最新主版本,全面支持OAuth 2.1和OpenID Connect 1.0。本文将实战演示如何构建一个完整的OAuth 2.0授权服务器和资源服务器,实现安全的API认证与授权。
二、项目架构设计
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 客户端应用 │────▶│ OAuth 2.0 │────▶│ 资源服务器 │
│ (Vue/React) │◀────│ 授权服务器 │◀────│ (REST API) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┴───────────────────────┘
JWT Token 认证流程
三、授权服务器搭建
3.1 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.2 授权服务器配置
@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {
@Bean @Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
return http.build();
}
@Bean @Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.requestMatchers("/login").permitAll().anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("my-client").clientSecret("{noop}my-secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/my-client")
.scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).scope("api.read").scope("api.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();
return new InMemoryRegisteredClientRepository(client);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsaKey();
return (jwkSelector, context) -> jwkSelector.select(new JWKSet(rsaKey));
}
private static RSAKey generateRsaKey() {
try {
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
return new RSAKey.Builder((RSAPublicKey)keyPair.getPublic())
.privateKey((RSAPrivateKey)keyPair.getPrivate()).keyID(UUID.randomUUID().toString()).build();
} catch (Exception e) { throw new IllegalStateException(e); }
}
}
四、资源服务器搭建
4.1 配置文件
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:9000
jwk-set-uri: http://localhost:9000/.well-known/jwks.json
4.2 安全配置
@Configuration
@EnableWebSecurity
public class ResourceServerConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
4.3 受保护API示例
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/user/profile")
public Map<String, Object> userProfile(@AuthenticationPrincipal Jwt jwt) {
return Map.of("username", jwt.getSubject(), "scopes", jwt.getClaimAsStringList("scope"));
}
@GetMapping("/admin/data")
@PreAuthorize("hasRole('ADMIN')")
public String adminData() { return "Admin only data"; }
}
五、客户端测试
5.1 获取Token
curl -X POST http://localhost:9000/oauth2/token -H "Content-Type: application/x-www-form-urlencoded" -u "my-client:my-secret" -d "grant_type=client_credentials" -d "scope=api.read"
# 返回: {"access_token":"eyJ...","token_type":"Bearer","expires_in":300}
5.2 调用API
curl -H "Authorization: Bearer eyJ..." http://localhost:8081/api/user/profile
# 返回: {"username":"admin","scopes":["openid","profile","api.read"]}
六、最佳实践
- HTTPS:生产环境必须使用TLS
- Token过期:access_token建议5-15分钟
- PKCE:移动端和SPA必须使用
- 密钥管理:使用Vault等工具存储密钥
七、总结
本文完整演示了Spring Security 6搭建OAuth 2.0授权服务器和资源服务器的全过程。Spring Security 6简化了OAuth 2.0的配置,建议结合Redis实现Token存储,使用数据库存储客户端和用户数据。
文章摘自:https://www.cnblogs.com/czlws/p/19846673/spring-security-oauth2-tutorial-2026
