Spring Security 6 + OAuth 2.0实战:构建企业级认证授权系统

一、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"]}

六、最佳实践

  1. HTTPS:生产环境必须使用TLS
  2. Token过期:access_token建议5-15分钟
  3. PKCE:移动端和SPA必须使用
  4. 密钥管理:使用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