3. Spring Security OAuth2.0 集成:基于 Spring Boot 3.x 搭建授权服务器与资源服务器核心配置

在微服务架构中,授权服务器(Authorization Server)与资源服务器(Resource Server)是 OAuth2.0 协议的两大核心角色。Spring Boot 3.x 基于 Spring Security 6.x 重构了 OAuth2 支持,移除了已废弃的 spring-security-oauth2 模块,全面转向 spring-security-oauth2-authorization-server。本章将演示如何从零搭建这两个核心组件,并完成令牌签发与验证的闭环。

3.1 架构概览:授权服务器与资源服务器的协作关系

在典型的 OAuth2.0 流程中,授权服务器负责客户端认证、用户授权与令牌发放;资源服务器负责验证令牌并保护 API 端点。两者通过共享的令牌签名密钥或远程验证端点建立信任。

授权服务器与资源服务器协作架构 客户端 (Client) 授权服务器 Authorization Server /oauth2/authorize 资源服务器 Resource Server /api/orders 令牌端点 /oauth2/token 1. 授权请求 2. 授权码 3. 返回 Access Token 4. 携带令牌请求资源 5. 令牌验证
架构要点:在 Spring Boot 3.x 中,授权服务器与资源服务器可以是两个独立的进程,也可以部署在同一进程内。生产环境建议分离部署,资源服务器通过 spring.security.oauth2.resourceserver.jwt.issuer-uri 配置远程验证端点,避免密钥硬编码。

3.2 搭建授权服务器(Authorization Server)

Spring Boot 3.x 使用 spring-security-oauth2-authorization-server 作为授权服务器的官方实现。以下为核心配置步骤。

3.2.1 添加 Maven 依赖

<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>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>1.1.0</version>
</dependency>

3.2.2 授权服务器配置类

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig {

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId("payment-client")
                .clientId("payment-app")
                .clientSecret("{noop}secret-key-123")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://localhost:8081/login/oauth2/code/payment")
                .scope("read:orders")
                .scope("write:orders")
                .tokenSettings(TokenSettings.builder()
                        .accessTokenTimeToLive(Duration.ofMinutes(30))
                        .refreshTokenTimeToLive(Duration.ofDays(1))
                        .build())
                .build();

        return new InMemoryRegisteredClientRepository(registeredClient);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = Jwks.generateRsaKey();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder()
                .issuer("http://auth-server:9000")
                .build();
    }
}
安全警告:示例中使用 {noop} 前缀表示明文密码,仅用于开发调试。生产环境必须使用 {bcrypt} 或 {scrypt} 编码的密码。RSA 密钥对应存储在硬件安全模块(HSM)或密钥管理服务(KMS)中,避免硬编码在代码仓库。

3.2.3 生成 RSA 密钥工具类

public final class Jwks {

    private Jwks() {}

    public static RSAKey generateRsaKey() {
        KeyPair keyPair = KeyGenerator.generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
    }

    static class KeyGenerator {
        static KeyPair generateRsaKey() {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            return keyPairGenerator.generateKeyPair();
        }
    }
}

3.3 搭建资源服务器(Resource Server)

资源服务器负责验证 JWT 令牌的签名、有效期和作用域,并据此控制 API 访问权限。

3.3.1 添加 Maven 依赖

<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>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

3.3.2 资源服务器配置类

@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/orders/**").hasAuthority("SCOPE_read:orders")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .decoder(jwtDecoder())
                )
            );
        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder
                .withJwkSetUri("http://auth-server:9000/oauth2/jwks")
                .build();
    }
}

3.3.3 application.yml 配置

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://auth-server:9000
          jwk-set-uri: http://auth-server:9000/oauth2/jwks

server:
  port: 8081

3.4 令牌验证机制对比

验证方式 原理 适用场景 性能 安全等级
本地 JWT 解码 资源服务器缓存授权服务器的 JWKS 公钥,本地验证签名 高并发、低延迟要求的内部服务 高(无网络开销) 高(公钥定期轮换)
远程令牌自省 资源服务器调用授权服务器的 /oauth2/introspect 端点验证 令牌格式非 JWT、需要实时撤销的场景 中(每次验证需网络请求) 最高(实时状态查询)
共享数据库 授权服务器与资源服务器共享令牌存储表 遗留系统、令牌格式不统一 低(数据库瓶颈) 中(数据库安全依赖)
推荐实践:在支付订单场景中,建议采用 本地 JWT 解码 + 短令牌有效期(15-30 分钟) 的组合策略。既保证高并发下的验证性能,又通过短有效期降低令牌泄露风险。对于需要即时撤销的敏感操作(如退款),可额外引入远程自省端点做二次校验。

3.5 作用域(Scope)与权限控制

OAuth2.0 的作用域是资源服务器进行细粒度授权的基础。在支付订单场景中,建议按操作类型划分作用域:

  • read:orders — 查询订单状态、历史记录
  • write:orders — 创建新订单、修改订单信息
  • refund:orders — 执行退款操作(高风险,需额外审批)
  • admin:orders — 管理级操作,如订单删除、费率调整
@GetMapping("/api/orders/{id}")
@PreAuthorize("hasAuthority('SCOPE_read:orders')")
public Order getOrder(@PathVariable String id) {
    return orderService.findById(id);
}

@PostMapping("/api/orders")
@PreAuthorize("hasAuthority('SCOPE_write:orders')")
public Order createOrder(@RequestBody OrderRequest request) {
    return orderService.create(request);
}

@PostMapping("/api/orders/{id}/refund")
@PreAuthorize("hasAuthority('SCOPE_refund:orders')")
public RefundResult refundOrder(@PathVariable String id) {
    return orderService.refund(id);
}

3.6 令牌端点与授权流程

授权服务器启动后,默认暴露以下核心端点:

端点路径 HTTP 方法 用途 认证方式
/oauth2/authorize GET 用户授权页面,获取授权码 用户会话
/oauth2/token POST 交换授权码获取 Access Token Client Secret Basic
/oauth2/jwks GET 公开 JWKS 公钥集合 无
/oauth2/introspect POST 令牌自省(验证令牌状态) Client Secret Basic
/oauth2/revoke POST 撤销令牌 Client Secret Basic
调试技巧:开发阶段可使用 curl 快速测试授权码流程:
curl -X POST "http://auth-server:9000/oauth2/token" -H "Authorization: Basic cGF5bWVudC1hcHA6c2VjcmV0LWtleS0xMjM=" -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=http://localhost:8081/login/oauth2/code/payment"

3.7 安全加固要点

生产环境必读:以下配置项在支付场景中必须强制执行:
  • 强制 HTTPS:所有令牌传输必须通过 TLS 1.2+ 加密,防止令牌在传输中被截获。
  • 客户端认证强化:使用 mTLS(双向 TLS)替代简单的 Client Secret,尤其适用于服务间通信。
  • 令牌绑定:将 Access Token 与客户端 IP、User-Agent 或设备指纹绑定,防止令牌被盗用后跨设备使用。
  • 刷新令牌轮换:每次使用刷新令牌后,签发新的刷新令牌并废弃旧令牌,降低长期令牌泄露风险。
  • 作用域最小化:每个客户端只申请业务所需的最小作用域集合,避免过度授权。

3.8 完整配置清单

以下为授权服务器与资源服务器的关键配置项速查表,便于快速搭建和排错:

配置项 所属组件 说明 示例值
issuer-uri 资源服务器 授权服务器的颁发者标识,用于验证令牌的 iss 声明 http://auth-server:9000
jwk-set-uri 资源服务器 获取授权服务器公钥的端点 http://auth-server:9000/oauth2/jwks
access-token-time-to-live 授权服务器 Access Token 有效期 30m
refresh-token-time-to-live 授权服务器 Refresh Token 有效期 1d
client-authentication-method 授权服务器 客户端认证方式 client_secret_basic
authorization-grant-type 授权服务器 支持的授权类型 authorization_code
本章小结:通过 Spring Boot 3.x + Spring Security OAuth2 Authorization Server,我们成功搭建了授权服务器与资源服务器的核心骨架。授权服务器使用 RSA 非对称签名签发 JWT 令牌,资源服务器通过 JWKS 端点获取公钥进行本地验证。下一环节将在此基础上引入链路追踪中间件,实现令牌在微服务调用链中的透明传递与审计。