跳到主要内容
版本:7.0.2

OAuth2 WebFlux

DeepSeek V3 中英对照 OAuth2 OAuth2 WebFlux

Spring Security 提供了全面的 OAuth 2.0 支持。本节将讨论如何将 OAuth 2.0 集成到您的响应式应用程序中。

概述

Spring Security 的 OAuth 2.0 支持主要由两大功能集构成:

备注

OAuth2 登录 是一个非常强大的 OAuth2 客户端功能,值得在参考文档中拥有独立的章节。然而,它并非作为一个独立的功能存在,需要 OAuth2 客户端才能运行。

这些功能集涵盖了 OAuth 2.0 授权框架 中定义的资源服务器客户端角色,而授权服务器角色则由 Spring Authorization Server 涵盖,这是一个基于 Spring Security 构建的独立项目。

在 OAuth2 中,资源服务器客户端角色通常由一个或多个服务器端应用程序表示。此外,授权服务器角色可以由一个或多个第三方表示(例如在组织内集中管理身份和/或认证时)——或者—— 也可以由一个应用程序表示(例如 Spring Authorization Server 的情况)。

例如,一个典型的基于OAuth2的微服务架构可能包含一个面向用户的客户端应用程序、多个提供REST API的后端资源服务器,以及一个用于管理用户和认证事宜的第三方授权服务器。同样常见的情况是,单个应用程序仅代表这些角色之一,需要与提供其他角色的一个或多个第三方进行集成。

Spring Security 能够处理这些场景以及更多情况。以下章节将介绍 Spring Security 提供的角色,并包含常见场景的示例。

OAuth2 资源服务器

备注

本节包含 OAuth2 资源服务器功能的概览及示例。完整参考文档请参阅 OAuth 2.0 资源服务器

要开始使用,请将 spring-security-oauth2-resource-server 依赖添加到你的项目中。如果使用 Spring Boot,请添加以下 starter:

implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
提示

在不使用 Spring Boot 时,请参阅 获取 Spring Security 了解其他选项。

考虑以下OAuth2资源服务器的使用场景:

使用 OAuth2 访问令牌保护访问

使用 OAuth2 访问令牌保护 API 访问是非常常见的做法。在大多数情况下,Spring Security 只需极简配置即可通过 OAuth2 保护应用程序。

Spring Security 支持两种类型的 Bearer 令牌,每种类型使用不同的组件进行验证:

  • JWT 支持 使用 ReactiveJwtDecoder bean 来验证签名和解码令牌

  • 不透明令牌支持 使用 ReactiveOpaqueTokenIntrospector bean 来内省令牌

JWT 支持

以下示例使用Spring Boot配置属性配置了一个ReactiveJwtDecoder bean:

spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://my-auth-server.com

在使用Spring Boot时,仅需如此配置即可。Spring Boot提供的默认配置等效于以下内容:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}

@Bean
public ReactiveJwtDecoder jwtDecoder() {
return ReactiveJwtDecoders.fromIssuerLocation("https://my-auth-server.com");
}

}

不透明令牌支持

以下示例使用 Spring Boot 配置属性配置了一个 ReactiveOpaqueTokenIntrospector bean:

spring:
security:
oauth2:
resourceserver:
opaquetoken:
introspection-uri: https://my-auth-server.com/oauth2/introspect
client-id: my-client-id
client-secret: my-client-secret

在使用Spring Boot时,仅需如此配置即可。Spring Boot提供的默认配置等同于以下设置:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.opaqueToken(Customizer.withDefaults())
);
return http.build();
}

@Bean
public ReactiveOpaqueTokenIntrospector opaqueTokenIntrospector() {
return new SpringReactiveOpaqueTokenIntrospector(
"https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret");
}

}

使用自定义 JWT 保护访问

使用JWT保护API访问是一个相当普遍的目标,尤其是在前端开发为单页应用的情况下。Spring Security中的OAuth2资源服务器支持可用于任何类型的Bearer令牌,包括自定义JWT。

使用JWT保护API所需的全部配置就是一个ReactiveJwtDecoder bean,它用于验证签名并解码令牌。Spring Security 将自动使用提供的 bean 来配置 SecurityWebFilterChain 中的保护机制。

以下示例使用 Spring Boot 配置属性来配置一个 ReactiveJwtDecoder bean:

spring:
security:
oauth2:
resourceserver:
jwt:
public-key-location: classpath:my-public-key.pub
备注

你可以将公钥作为类路径资源提供(在本示例中称为 my-public-key.pub)。

在使用Spring Boot时,仅需如此配置即可。Spring Boot提供的默认配置等效于以下内容:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}

@Bean
public ReactiveJwtDecoder jwtDecoder() {
return NimbusReactiveJwtDecoder.withPublicKey(publicKey()).build();
}

private RSAPublicKey publicKey() {
// ...
}

}
备注

Spring Security 并未提供用于生成令牌的端点。然而,Spring Security 确实提供了 JwtEncoder 接口以及一个实现,即 NimbusJwtEncoder

OAuth2 客户端

备注

本节包含 OAuth2 客户端功能的概览及示例。完整参考文档请参阅 OAuth 2.0 客户端OAuth 2.0 登录

要开始使用,请将 spring-security-oauth2-client 依赖项添加到你的项目中。如果使用 Spring Boot,请添加以下 starter:

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
提示

在不使用 Spring Boot 的情况下,如需了解更多选项,请参阅 获取 Spring Security

考虑以下OAuth2客户端的用例:

使用 OAuth2 登录用户

通过 OAuth2 要求用户登录是非常常见的做法。OpenID Connect 1.0 提供了一种特殊的令牌,称为 id_token,其设计目的是让 OAuth2 客户端能够执行用户身份验证并让用户登录。在某些情况下,OAuth2 也可以直接用于用户登录(例如,像 GitHub 和 Facebook 这样未实现 OpenID Connect 的流行社交登录提供商就是如此)。

以下示例配置应用程序,使其能够作为 OAuth2 客户端,支持用户通过 OAuth2 或 OpenID Connect 登录:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
// ...
.oauth2Login(Customizer.withDefaults());
return http.build();
}

}

除了上述配置外,应用程序还需要通过使用 ReactiveClientRegistrationRepository bean 来配置至少一个 ClientRegistration。以下示例使用 Spring Boot 配置属性配置了一个 InMemoryReactiveClientRegistrationRepository bean:

spring:
security:
oauth2:
client:
registration:
my-oidc-client:
provider: my-oidc-provider
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: openid,profile
provider:
my-oidc-provider:
issuer-uri: https://my-oidc-provider.com

通过以上配置,应用程序现在支持两个额外的端点:

  1. 登录端点(例如 /oauth2/authorization/my-oidc-client)用于发起登录并重定向到第三方授权服务器。

  2. 重定向端点(例如 /login/oauth2/code/my-oidc-client)由授权服务器用于重定向回客户端应用程序,并将包含一个 code 参数,该参数用于通过访问令牌请求获取 id_token 和/或 access_token

备注

上述配置中包含 openid 作用域,表明应使用 OpenID Connect 1.0。这会指示 Spring Security 在请求处理过程中使用 OIDC 专用组件(例如 OidcReactiveOAuth2UserService)。若未包含此作用域,Spring Security 将改用 OAuth2 专用组件(例如 DefaultReactiveOAuth2UserService)。

访问受保护资源

向受 OAuth2 保护的第三方 API 发起请求是 OAuth2 Client 的核心使用场景。这通常通过授权一个客户端(在 Spring Security 中由 OAuth2AuthorizedClient 类表示)来实现,并在对外请求的 Authorization 头部中放置一个 Bearer 令牌来访问受保护的资源。

以下示例配置应用程序作为 OAuth2 客户端,使其能够向第三方 API 请求受保护的资源:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
// ...
.oauth2Client(Customizer.withDefaults());
return http.build();
}

}
备注

上述示例并未提供用户登录的方式。你可以使用任何其他登录机制(例如 formLogin())。关于将 oauth2Client()oauth2Login() 结合使用的示例,请参阅下一节

除了上述配置外,应用程序还需要通过使用 ReactiveClientRegistrationRepository bean 来配置至少一个 ClientRegistration。以下示例使用 Spring Boot 配置属性配置了一个 InMemoryReactiveClientRegistrationRepository bean:

spring:
security:
oauth2:
client:
registration:
my-oauth2-client:
provider: my-auth-server
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: message.read,message.write
provider:
my-auth-server:
issuer-uri: https://my-auth-server.com

除了配置Spring Security以支持OAuth2客户端功能外,您还需要决定如何访问受保护资源,并相应配置您的应用程序。Spring Security提供了ReactiveOAuth2AuthorizedClientManager的实现,用于获取可用于访问受保护资源的访问令牌。

提示

当不存在 ReactiveOAuth2AuthorizedClientManager 时,Spring Security 会为你注册一个默认的 bean。

使用 ReactiveOAuth2AuthorizedClientManager 最简单的方式是通过一个 ExchangeFilterFunction,它通过 WebClient 拦截请求。

以下示例使用默认的 ReactiveOAuth2AuthorizedClientManager 来配置一个 WebClient,该客户端能够通过在每个请求的 Authorization 头部放置 Bearer 令牌来访问受保护资源:

@Configuration
public class WebClientConfig {

@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(filter)
.build();
}

}

配置好的 WebClient 可按以下示例使用:

import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;

@RestController
public class MessagesController {

private final WebClient webClient;

public MessagesController(WebClient webClient) {
this.webClient = webClient;
}

@GetMapping("/messages")
public Mono<ResponseEntity<List<Message>>> messages() {
return this.webClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.retrieve()
.toEntityList(Message.class);
}

public record Message(String message) {
}

}

访问当前用户的受保护资源

当用户通过OAuth2或OpenID Connect登录时,授权服务器可能会提供一个访问令牌,该令牌可直接用于访问受保护资源。这种方式非常便捷,因为它只需为这两种用例同时配置一个 ClientRegistration

备注

本节将使用 OAuth2 登录用户访问受保护资源的配置合并为一个单一配置。还存在其他高级场景,例如配置一个 ClientRegistration 用于登录,另一个用于访问受保护资源。所有这些场景都将使用相同的基础配置。

以下示例配置应用程序作为 OAuth2 客户端,使其能够登录用户从第三方 API 请求受保护资源:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
// ...
.oauth2Login(Customizer.withDefaults())
.oauth2Client(Customizer.withDefaults());
return http.build();
}

}

除了上述配置外,应用程序还需要通过 ReactiveClientRegistrationRepository bean 配置至少一个 ClientRegistration。以下示例使用 Spring Boot 配置属性配置了一个 InMemoryReactiveClientRegistrationRepository bean:

spring:
security:
oauth2:
client:
registration:
my-combined-client:
provider: my-auth-server
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: openid,profile,message.read,message.write
provider:
my-auth-server:
issuer-uri: https://my-auth-server.com
备注

之前示例(使用 OAuth2 登录用户访问受保护资源)与本示例的主要区别在于通过 scope 属性配置的内容,该属性将标准范围 openidprofile 与自定义范围 message.readmessage.write 相结合。

除了配置Spring Security以支持OAuth2客户端功能外,您还需要决定如何访问受保护资源,并相应地配置您的应用程序。Spring Security提供了ReactiveOAuth2AuthorizedClientManager的实现,用于获取可用于访问受保护资源的访问令牌。

提示

当不存在 ReactiveOAuth2AuthorizedClientManager 时,Spring Security 会为您注册一个默认的 bean。

使用 ReactiveOAuth2AuthorizedClientManager 最简单的方式是通过一个 ExchangeFilterFunction,它通过 WebClient 拦截请求。

以下示例使用默认的 ReactiveOAuth2AuthorizedClientManager 来配置一个 WebClient,该客户端能够通过在每个请求的 Authorization 头部放置 Bearer 令牌来访问受保护资源:

@Configuration
public class WebClientConfig {

@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(filter)
.build();
}

}

配置好的 WebClient 可按以下示例使用:

@RestController
public class MessagesController {

private final WebClient webClient;

public MessagesController(WebClient webClient) {
this.webClient = webClient;
}

@GetMapping("/messages")
public Mono<ResponseEntity<List<Message>>> messages() {
return this.webClient.get()
.uri("http://localhost:8090/messages")
.retrieve()
.toEntityList(Message.class);
}

public record Message(String message) {
}

}
备注

之前的示例不同,请注意我们无需告知 Spring Security 要使用的 clientRegistrationId。这是因为该信息可以从当前已登录的用户中推导出来。

启用扩展授权类型

一个常见用例涉及启用和/或配置扩展授权类型。例如,Spring Security 提供了对 jwt-bearertoken-exchange 授权类型的支持,但默认情况下并未启用它们,因为它们不属于 OAuth 2.0 核心规范的一部分。

从 Spring Security 6.3 版本开始,我们可以简单地发布一个或多个 ReactiveOAuth2AuthorizedClientProvider 的 Bean,它们将被自动识别。以下示例仅启用了 jwt-bearer 授权类型:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AuthorizedClientProvider jwtBearer() {
return new JwtBearerReactiveOAuth2AuthorizedClientProvider();
}

}

当未提供 ReactiveOAuth2AuthorizedClientManager 时,Spring Security 会自动发布一个默认实例。

提示

任何自定义的 OAuth2AuthorizedClientProvider bean 也会被自动发现,并在默认授权类型之后应用于所提供的 ReactiveOAuth2AuthorizedClientManager

为了在 Spring Security 6.3 之前实现上述配置,我们必须自行发布这个 bean,并确保重新启用默认授权类型。要理解背后配置的具体内容,以下展示了可能的配置方式:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.provider(new JwtBearerReactiveOAuth2AuthorizedClientProvider())
.build();

DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

return authorizedClientManager;
}

}

自定义现有授权类型

通过发布一个 bean 来启用扩展授权类型的能力,也提供了在不重新定义默认配置的情况下自定义现有授权类型的机会。例如,如果我们想要为 client_credentials 授权自定义 ReactiveOAuth2AuthorizedClientProvider 的时钟偏移量,我们可以简单地发布一个如下所示的 bean:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AuthorizedClientProvider clientCredentials() {
ClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();
authorizedClientProvider.setClockSkew(Duration.ofMinutes(5));

return authorizedClientProvider;
}

}

自定义令牌请求参数

在获取访问令牌时,自定义请求参数的需求相当常见。例如,假设我们想要在令牌请求中添加一个自定义的 audience 参数,因为提供方要求 authorization_code 授权类型必须包含此参数。

我们可以简单地发布一个类型为 ReactiveOAuth2AccessTokenResponseClient 且泛型为 OAuth2AuthorizationCodeGrantRequest 的 Bean,Spring Security 将使用它来配置 OAuth2 客户端组件。

以下示例为 authorization_code 授权类型自定义令牌请求参数:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new WebClientReactiveAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.addParametersConverter(parametersConverter());

return accessTokenResponseClient;
}

private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
return (grantRequest) -> {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set("audience", "xyz_value");

return parameters;
};
}

}
提示

请注意,在这种情况下我们不需要自定义 SecurityWebFilterChain bean,可以保持默认配置。如果使用 Spring Boot 且没有额外的自定义配置,实际上我们可以完全省略 SecurityWebFilterChain bean。

如你所见,将 ReactiveOAuth2AccessTokenResponseClient 作为 Bean 提供非常方便。当直接使用 Spring Security DSL 时,我们需要确保此自定义配置同时应用于 OAuth2 登录(如果我们正在使用此功能)和 OAuth2 客户端组件。为了理解幕后配置的内容,以下是使用 DSL 时的配置示例:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new WebClientReactiveAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.addParametersConverter(parametersConverter());

http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.oauth2Login((oauth2Login) -> oauth2Login
.authenticationManager(new DelegatingReactiveAuthenticationManager(
new OidcAuthorizationCodeReactiveAuthenticationManager(
accessTokenResponseClient, new OidcReactiveOAuth2UserService()
),
new OAuth2LoginReactiveAuthenticationManager(
accessTokenResponseClient, new DefaultReactiveOAuth2UserService()
)
))
)
.oauth2Client((oauth2Client) -> oauth2Client
.authenticationManager(new OAuth2AuthorizationCodeReactiveAuthenticationManager(
accessTokenResponseClient
))
);

return http.build();
}

private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
// ...
}

}

对于其他授权类型,我们可以发布额外的 ReactiveOAuth2AccessTokenResponseClient Bean 来覆盖默认配置。例如,要自定义 client_credentials 授权类型的令牌请求,我们可以发布以下 Bean:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =
new WebClientReactiveClientCredentialsTokenResponseClient();
accessTokenResponseClient.addParametersConverter(parametersConverter());

return accessTokenResponseClient;
}

private static Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter() {
// ...
}

}

Spring Security 会自动解析以下泛型类型的 ReactiveOAuth2AccessTokenResponseClient Bean:

  • OAuth2AuthorizationCodeGrantRequest (参见 WebClientReactiveAuthorizationCodeTokenResponseClient)

  • OAuth2RefreshTokenGrantRequest (参见 WebClientReactiveRefreshTokenTokenResponseClient)

  • OAuth2ClientCredentialsGrantRequest (参见 WebClientReactiveClientCredentialsTokenResponseClient)

  • JwtBearerGrantRequest (参见 WebClientReactiveJwtBearerTokenResponseClient)

  • TokenExchangeGrantRequest (参见 WebClientReactiveTokenExchangeTokenResponseClient)

提示

发布一个类型为 ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> 的 bean 将自动启用 jwt-bearer 授权类型,而无需单独配置

提示

发布一个类型为 ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> 的 bean 将自动启用 token-exchange 授权类型,而无需单独配置

自定义 OAuth2 客户端组件使用的 WebClient

另一个常见用例是需要自定义获取访问令牌时使用的 WebClient。我们可能需要这样做,以便自定义底层的 HTTP 客户端库(通过自定义的 ClientHttpConnector)来配置 SSL 设置,或为企业网络应用代理设置。

从 Spring Security 6.3 版本开始,我们可以直接发布 ReactiveOAuth2AccessTokenResponseClient 类型的 Bean,Spring Security 将自动为我们配置并发布一个 ReactiveOAuth2AuthorizedClientManager Bean。

以下示例为所有支持的授权类型自定义了 WebClient

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new WebClientReactiveAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
WebClientReactiveRefreshTokenTokenResponseClient accessTokenResponseClient =
new WebClientReactiveRefreshTokenTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =
new WebClientReactiveClientCredentialsTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
WebClientReactiveJwtBearerTokenResponseClient accessTokenResponseClient =
new WebClientReactiveJwtBearerTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {
WebClientReactiveTokenExchangeTokenResponseClient accessTokenResponseClient =
new WebClientReactiveTokenExchangeTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public WebClient webClient() {
// ...
}

}

当未提供 ReactiveOAuth2AuthorizedClientManager 时,Spring Security 会自动发布一个默认实例。

提示

请注意,在这种情况下我们不需要自定义 SecurityWebFilterChain bean,可以继续使用默认配置。如果使用 Spring Boot 且没有额外的自定义配置,实际上我们可以完全省略 SecurityWebFilterChain bean。

在Spring Security 6.3版本之前,我们必须自行确保此自定义配置应用于OAuth2客户端组件。虽然我们可以为authorization_code授权类型发布一个ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>类型的bean,但对于其他授权类型,我们必须发布一个ReactiveOAuth2AuthorizedClientManager类型的bean。为了理解背后配置的具体内容,以下展示了可能的配置示例:

@Configuration
public class SecurityConfig {

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new WebClientReactiveAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient());

return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

WebClientReactiveRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =
new WebClientReactiveRefreshTokenTokenResponseClient();
refreshTokenAccessTokenResponseClient.setWebClient(webClient());

WebClientReactiveClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =
new WebClientReactiveClientCredentialsTokenResponseClient();
clientCredentialsAccessTokenResponseClient.setWebClient(webClient());

WebClientReactiveJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =
new WebClientReactiveJwtBearerTokenResponseClient();
jwtBearerAccessTokenResponseClient.setWebClient(webClient());

JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
new JwtBearerReactiveOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient);

WebClientReactiveTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =
new WebClientReactiveTokenExchangeTokenResponseClient();
tokenExchangeAccessTokenResponseClient.setWebClient(webClient());

TokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
new TokenExchangeReactiveOAuth2AuthorizedClientProvider();
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient);

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken((refreshToken) -> refreshToken
.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
)
.clientCredentials((clientCredentials) -> clientCredentials
.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
)
.provider(jwtBearerAuthorizedClientProvider)
.provider(tokenExchangeAuthorizedClientProvider)
.build();

DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

return authorizedClientManager;
}

@Bean
public WebClient webClient() {
// ...
}

}

延伸阅读

前面的章节通过常见场景示例介绍了Spring Security对OAuth2的支持。您可以在参考文档的以下章节中阅读更多关于OAuth2客户端和资源服务器的内容:

章节总结