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 端点。两者通过共享的令牌签名密钥或远程验证端点建立信任。
架构要点:在 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 端点获取公钥进行本地验证。下一环节将在此基础上引入链路追踪中间件,实现令牌在微服务调用链中的透明传递与审计。