SpringSecurity中文文档(Servlet OAuth2)

发布于:2024-07-11 ⋅ 阅读:(19) ⋅ 点赞:(0)

OAuth2

Spring Security 提供了全面的 OAuth 2.0支持。

Overview

Spring Security 的 OAuth 2.0支持包括两个主要特性集:

OAuth2Login 是一个非常强大的 OAuth2Client 特性,值得在参考文档中单独列出。但是,它不是作为一个独立的特性存在的,需要 OAuth2 Client 才能运行。

这些特性集涵盖了 OAuth 2.0 Authorization Framework 中定义的资源服务器和客户机角色,而 Spring Authorization Server 涵盖了授权服务器角色,后者是构建在 Spring Security 上的一个独立项目。

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

例如,典型的基于 OAuth2的微服务体系结构可能包括一个面向用户的客户端应用程序、几个提供 REST API 的后端资源服务器和一个用于管理用户和身份验证问题的第三方授权服务器。一个应用程序只代表其中一个角色,并且需要与提供其他角色的一个或多个第三方集成,这种情况也很常见。

SpringSecurity 处理这些场景等等。以下各节介绍 SpringSecurity 提供的角色,并包含常见场景的示例。

OAuth2 Resource Server

本节包含 OAuth2资源服务器特性的摘要和示例。有关完整的参考文档,请参阅 OAuth 2.0 Resource Server

首先,将 Spring-security-oauth2-resource-server 依赖项添加到项目中:

OAuth2 Client with Spring Boot

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

考虑 OAuth2资源服务器的以下用例:

Protect Access with an OAuth2 Access Token

使用 OAuth2访问令牌保护对 API 的访问是非常常见的。在大多数情况下,SpringSecurity 只需要最小的配置就可以使用 OAuth2保护应用程序。

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

  • JWT 支持使用 JwtDecoderbean 来验证签名和解码令牌
  • 不透明令牌支持使用一个 OpaqueTokenIntrospector来自省令牌
JWT Support

下面的示例使用 Spring Boot 配置属性配置一个 JwtDecode bean:

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

在使用 SpringBoot 时,这就是所需的全部内容。Spring Boot 提供的默认安排相当于以下内容:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.oauth2ResourceServer((oauth2) -> oauth2
				.jwt(Customizer.withDefaults())
			);
		return http.build();
	}

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

}
Opaque Token Support

下面的示例使用 Spring Boot 配置属性配置 OpaqueTokenIntrospector 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

在使用 SpringBoot 时,这就是所需的全部内容。Spring Boot 提供的默认安排相当于以下内容:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.oauth2ResourceServer((oauth2) -> oauth2
				.opaqueToken(Customizer.withDefaults())
			);
		return http.build();
	}

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

}

Protect Access with a custom JWT

使用 JWT 保护对 API 的访问是一个相当普遍的目标,特别是当前端开发为单页应用时。Spring Security 中的 OAuth2 Resource Server 支持可用于任何类型的 Bearer 令牌,包括自定义 JWT。

使用 JWT 保护 API 所需的全部内容是一个 JwtDecoder bean,它用于验证签名和解码令牌。SpringSecurity 将自动使用提供的 bean 在 SecurityFilterChain 中配置保护。

下面的示例使用 Spring Boot 配置属性配置 JwtDecode bean:

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

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

在使用 SpringBoot 时,这就是所需的全部内容。Spring Boot 提供的默认安排相当于以下内容:

Configure Resource Server with Custom JWTs

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.oauth2ResourceServer((oauth2) -> oauth2
				.jwt(Customizer.withDefaults())
			);
		return http.build();
	}

	@Bean
	public JwtDecoder jwtDecoder() {
		return NimbusJwtDecoder.withPublicKey(publicKey()).build();
	}

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

SpringSecurity 不为创建令牌提供端点。但是,SpringSecurity 确实提供了 JwtEncoder 接口和一个实现,即 NimbusJwtEncoder。

OAuth2 Client

本节包含 OAuth2客户端特性的摘要和示例。有关完整的参考文档,请参阅 OAuth 2.0 Client 和 OAuth 2.0 Login。

OAuth2 Client with Spring Boot

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

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

Log Users In with OAuth2

要求用户通过 OAuth2登录是非常常见的。OpenID Connect 1.0提供了一个名为 id _ token 的特殊令牌,该令牌旨在为 OAuth2客户机提供执行用户身份验证和登录用户的能力。在某些情况下,OAuth2可以直接用于登录用户(与流行的不实现 OpenID Connect 的社交登录提供商(如 GitHub 和 Facebook)一样)。

以下示例将应用程序配置为能够用 OAuth2或 OpenID Connect 登录用户的 OAuth2客户端:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.oauth2Login(Customizer.withDefaults());
		return http.build();
	}
}

除了上述配置之外,应用程序还需要通过使用 ClientRegistrationRepository bean 至少配置一个 ClientRegistry。下面的示例使用 Spring 引导配置属性配置 InMemoryClientRegistrationRepository 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/authority/my-oidc-client)用于启动登录并重定向到第三方授权服务器。
  2. 重定向端点(例如/login/oauth2/code/my-oidc-client)被授权服务器用于重定向回客户端应用程序,并且将包含一个代码参数,用于通过访问令牌请求获取 id_token 和/或 access_token。

在上面的配置中出现 OpenID 作用域表明应该使用 OpenID Connect 1.0。这指示 SpringSecurity 在请求处理期间使用 OIDC 特定的组件(例如 OidcUserService)。如果没有这个作用域,SpringSecurity 将改为使用 OAuth2特定的组件(例如 DefaultOAuth2UserService)。

Access Protected Resources

向受 OAuth2保护的第三方 API 发出请求是 OAuth2客户机的核心用例。这是通过授权客户端(由 Spring Security 中的 OAuth2AuthorizedClient 类表示)和通过在出站请求的 Authorization 头中放置一个 Bearer 令牌来访问受保护的资源来实现的。

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

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.oauth2Client(Customizer.withDefaults());
		return http.build();
	}

}

上面的示例没有提供用户登录的方法。您可以使用任何其他登录机制(如 formLogin ())。有关 oauth2Client ()与 oauth2Login ()组合的示例,请参见下一节。

除了上述配置之外,应用程序还需要通过使用 ClientRegistrationRepository bean 至少配置一个 ClientRegistry。下面的示例使用 Spring 引导配置属性配置 InMemoryClientRegistrationRepository 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 Client 特性之外,您还需要决定如何访问受保护的资源并相应地配置应用程序。Spring Security 提供了 OAuth2AuthorizedClientManager 的实现,用于获取可用于访问受保护资源的访问令牌。

当 OAuth2AuthorizedClientManagerbean 不存在时,SpringSecurity 为您注册默认的 OAuth2AuthorizedClientManagerbean。

使用 OAuth2AuthorizedClientManager 的最简单方法是通过 ExchangeFilterfunction 拦截通过 WebClient 的请求。要使用 WebClient,您需要添加 spring-webflow 依赖项以及一个反应式客户端实现:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
	<groupId>io.projectreactor.netty</groupId>
	<artifactId>reactor-netty</artifactId>
</dependency>

下面的示例使用默认的 OAuth2AuthorizedClientManager 配置能够访问受保护资源的 WebClient,方法是在每个请求的 Authorization 头中放置 Bearer 令牌:

Configure WebClient with ExchangeFilterFunction

@Configuration
public class WebClientConfig {

	@Bean
	public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
		ServletOAuth2AuthorizedClientExchangeFilterFunction filter =
				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
		return WebClient.builder()
				.apply(filter.oauth2Configuration())
				.build();
	}

}

这个已配置的 WebClient 可以用于以下示例:

Use WebClient to Access Protected Resources

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

@RestController
public class MessagesController {

	private final WebClient webClient;

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

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

	public record Message(String message) {
	}

}

Access Protected Resources for the Current User

当用户通过 OAuth2或 OpenID Connect 登录时,授权服务器可以提供一个可以直接用于访问受保护资源的访问令牌。这很方便,因为它只需要为两个用例同时配置一个 ClientRegistry。

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

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

Configure OAuth2 Login and OAuth2 Client

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.oauth2Login(Customizer.withDefaults())
			.oauth2Client(Customizer.withDefaults());
		return http.build();
	}

}

除了上述配置之外,应用程序还需要通过使用 ClientRegistrationRepository bean 至少配置一个 ClientRegistry。下面的示例使用 Spring 引导配置属性配置 InMemoryClientRegistrationRepository 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

前面的示例(Log Users In with OAuth2,Access Protected Resources)和这个示例之间的主要区别是通过 scope 属性配置的,后者将标准作用域 openid 和 profile 与自定义作用域 message.read 和 message.write 结合在一起。

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

当 OAuth2AuthorizedClientManagerbean 不存在时,SpringSecurity 为您注册默认的 OAuth2AuthorizedClientManagerbean。

使用 OAuth2AuthorizedClientManager 的最简单方法是通过 ExchangeFilterfunction 拦截通过 WebClient 的请求。要使用 WebClient,您需要添加 spring-webflow 依赖项以及一个反应式客户端实现:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
	<groupId>io.projectreactor.netty</groupId>
	<artifactId>reactor-netty</artifactId>
</dependency>

下面的示例使用默认的 OAuth2AuthorizedClientManager 配置能够访问受保护资源的 WebClient,方法是在每个请求的 Authorization 头中放置 Bearer 令牌:

Configure WebClient with ExchangeFilterFunction

@Configuration
public class WebClientConfig {

	@Bean
	public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
		ServletOAuth2AuthorizedClientExchangeFilterFunction filter =
				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
		return WebClient.builder()
				.apply(filter.oauth2Configuration())
				.build();
	}

}

这个已配置的 WebClient 可以用于以下示例:

Use WebClient to Access Protected Resources (Current User)

@RestController
public class MessagesController {

	private final WebClient webClient;

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

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

	public record Message(String message) {
	}

}

与前面的示例不同,请注意我们不需要告诉 Spring Security 我们想要使用的 clientRegistrationId。这是因为它可以从当前登录的用户派生。

Enable an Extension Grant Type

一个常见的用例涉及到启用和/或配置扩展授予类型。例如,Spring Security 提供了对 jwt-bear 和令牌交换授权类型的支持,但是默认情况下不启用它们,因为它们不是核心 OAuth 2.0规范的一部分。

使用 Spring Security 6.2及更高版本,我们可以简单地为一个或多个 OAuth2AuthorizedClientProvider 发布 bean,它们将被自动拾取。下面的示例只是启用 jwt-bear 授予类型:

@Configuration
public class SecurityConfig {

	@Bean
	public OAuth2AuthorizedClientProvider jwtBearer() {
		return new JwtBearerOAuth2AuthorizedClientProvider();
	}

}

如果还没有提供默认的 OAuth2AuthorizedClientManager,Spring Security 将自动发布它。

任何自定义 OAuth2AuthorizedClientProviderbean 也将在默认授权类型之后被拾取并应用到所提供的 OAuth2AuthorizedClientManager。

Enable jwt-bearer Grant Type (prior to 6.2)

@Configuration
public class SecurityConfig {

	@Bean
	public OAuth2AuthorizedClientManager authorizedClientManager(
			ClientRegistrationRepository clientRegistrationRepository,
			OAuth2AuthorizedClientRepository authorizedClientRepository) {

		OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken()
				.clientCredentials()
				.password()
				.provider(new JwtBearerOAuth2AuthorizedClientProvider())
				.build();

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

		return authorizedClientManager;
	}

}

Customize an Existing Grant Type

通过发布 bean 启用扩展授权类型的能力还提供了定制现有授权类型的机会,而无需重新定义默认值。例如,如果我们想为 client _ credentials授权定制 OAuth2AuthorizedClientProvider 的时钟偏差,我们可以简单地发布一个 bean,如下所示:

@Configuration
public class SecurityConfig {

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

		return authorizedClientProvider;
	}

}

Customize Token Request Parameters

在获取访问令牌时需要自定义请求参数是相当常见的。例如,假设我们想要向令牌请求添加一个自定义的受众参数,因为提供程序需要这个参数才能授权authorization _ code。

使用 Spring Security 6.2及更高版本,我们可以简单地用泛型类型 OAuth2AuthorizationCodeGrantRequest 发布 OAuth2AccessTokenResponseClient 类型的 bean,Spring Security 将使用它来配置 OAuth2 Client 组件。

下面的示例为authorization_code授予而不使用 DSL 自定义令牌请求参数:

Customize Token Request Parameters for Authorization Code Grant

@Configuration
public class SecurityConfig {

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
		OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
			new OAuth2AuthorizationCodeGrantRequestEntityConverter();
		requestEntityConverter.addParametersConverter(parametersConverter());

		DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
			new DefaultAuthorizationCodeTokenResponseClient();
		accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);

		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;
		};
	}

}

注意,在这种情况下,我们不需要定制 SecurityFilterChain bean,可以使用默认值。如果在没有其他定制的情况下使用 SpringBoot,我们实际上可以完全省略 SecurityFilterChain bean。

在 Spring Security 6.2之前,我们必须确保使用 Spring Security DSL 对 OAuth2 Login (如果我们使用此特性)和 OAuth2 Client 组件都应用了此定制。要了解幕后配置是什么,以下是配置可能的样子:

Customize Token Request Parameters for Authorization Code Grant (prior to 6.2)

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
			new OAuth2AuthorizationCodeGrantRequestEntityConverter();
		requestEntityConverter.addParametersConverter(parametersConverter());

		DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
			new DefaultAuthorizationCodeTokenResponseClient();
		accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);

		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login((oauth2Login) -> oauth2Login
				.tokenEndpoint((tokenEndpoint) -> tokenEndpoint
					.accessTokenResponseClient(accessTokenResponseClient)
				)
			)
			.oauth2Client((oauth2Client) -> oauth2Client
				.authorizationCodeGrant((authorizationCode) -> authorizationCode
					.accessTokenResponseClient(accessTokenResponseClient)
				)
			);

		return http.build();
	}

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

}

对于其他授权类型,我们可以发布额外的 OAuth2AccessTokenResponseClient bean 来覆盖默认值。例如,要自定义 client _ credentials授予的令牌请求,我们可以发布以下 bean:

Customize Token Request Parameters for Client Credentials Grant

@Configuration
public class SecurityConfig {

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
		OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
			new OAuth2ClientCredentialsGrantRequestEntityConverter();
		requestEntityConverter.addParametersConverter(parametersConverter());

		DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
				new DefaultClientCredentialsTokenResponseClient();
		accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);

		return accessTokenResponseClient;
	}

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

}

Spring Security 自动解析以下通用类型的 OAuth2AccessTokenResponseClient bean:

  • OAuth2AuthorizationCodeGrantRequest (see DefaultAuthorizationCodeTokenResponseClient)
  • OAuth2RefreshTokenGrantRequest (see DefaultRefreshTokenTokenResponseClient)
  • OAuth2ClientCredentialsGrantRequest (see DefaultClientCredentialsTokenResponseClient)
  • OAuth2PasswordGrantRequest (see DefaultPasswordTokenResponseClient)
  • JwtBearerGrantRequest (see DefaultJwtBearerTokenResponseClient)
  • TokenExchangeGrantRequest (see DefaultTokenExchangeTokenResponseClient)

发布一个类型为 OAuth2AccessTokenResponseClient < JwtBearerGrantRequest > 的 bean 将自动启用 jwt-bear Grant 类型,而不需要单独配置它。

发布类型为 OAuth2AccessTokenResponseClient < TokenExchangeGrantRequest > 的 bean 将自动启用令牌交换授予类型,而无需单独配置它。

Customize the RestOperations used by OAuth2 Client Components

另一个常见的用例是需要自定义获取访问令牌时使用的 RestOperations。我们可能需要这样做来自定义响应处理(通过自定义 HttpMessageConverter)或为公司网络应用代理设置(通过自定义 ClientHttpRequestFactory)。

使用 Spring Security 6.2及更高版本,我们可以简单地发布 OAuth2AccessTokenResponseClient 类型的 bean,Spring Security 将为我们配置和发布 OAuth2AuthorizedClientManager bean。

下面的示例为所有受支持的授予类型自定义 RestOperations:

@Configuration
public class SecurityConfig {

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
		DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
			new DefaultAuthorizationCodeTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
		DefaultRefreshTokenTokenResponseClient accessTokenResponseClient =
			new DefaultRefreshTokenTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
		DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
			new DefaultClientCredentialsTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordAccessTokenResponseClient() {
		DefaultPasswordTokenResponseClient accessTokenResponseClient =
			new DefaultPasswordTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
		DefaultJwtBearerTokenResponseClient accessTokenResponseClient =
			new DefaultJwtBearerTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {
		DefaultTokenExchangeTokenResponseClient accessTokenResponseClient =
			new DefaultTokenExchangeTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		return accessTokenResponseClient;
	}

	@Bean
	public RestTemplate restTemplate() {
		// ...
	}

}

如果还没有提供默认的 OAuth2AuthorizedClientManager,Spring Security 将自动发布它。

在 Spring Security 6.2之前,我们必须确保这个定制同时应用于 OAuth2 Login (如果我们使用这个特性的话)和 OAuth2 Client 组件。我们必须同时使用 Spring Security DSL (对授权 _ 代码授权)和为其他授权类型发布 OAuth2AuthorizedClientManager 类型的 bean。要了解幕后配置是什么,以下是配置可能的样子:

Customize RestOperations for OAuth2 Client (prior to 6.2)

@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
			new DefaultAuthorizationCodeTokenResponseClient();
		accessTokenResponseClient.setRestOperations(restTemplate());

		http
			// ...
			.oauth2Login((oauth2Login) -> oauth2Login
				.tokenEndpoint((tokenEndpoint) -> tokenEndpoint
					.accessTokenResponseClient(accessTokenResponseClient)
				)
			)
			.oauth2Client((oauth2Client) -> oauth2Client
				.authorizationCodeGrant((authorizationCode) -> authorizationCode
					.accessTokenResponseClient(accessTokenResponseClient)
				)
			);

		return http.build();
	}

	@Bean
	public OAuth2AuthorizedClientManager authorizedClientManager(
			ClientRegistrationRepository clientRegistrationRepository,
			OAuth2AuthorizedClientRepository authorizedClientRepository) {

		DefaultRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =
			new DefaultRefreshTokenTokenResponseClient();
		refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate());

		DefaultClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =
			new DefaultClientCredentialsTokenResponseClient();
		clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate());

		DefaultPasswordTokenResponseClient passwordAccessTokenResponseClient =
			new DefaultPasswordTokenResponseClient();
		passwordAccessTokenResponseClient.setRestOperations(restTemplate());

		DefaultJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =
			new DefaultJwtBearerTokenResponseClient();
		jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate());

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

		DefaultTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =
			new DefaultTokenExchangeTokenResponseClient();
		tokenExchangeAccessTokenResponseClient.setRestOperations(restTemplate());

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

		OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken((refreshToken) -> refreshToken
					.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
				)
				.clientCredentials((clientCredentials) -> clientCredentials
					.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
				)
				.password((password) -> password
					.accessTokenResponseClient(passwordAccessTokenResponseClient)
				)
				.provider(jwtBearerAuthorizedClientProvider)
				.provider(tokenExchangeAuthorizedClientProvider)
				.build();

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

		return authorizedClientManager;
	}

	@Bean
	public RestTemplate restTemplate() {
		// ...
	}

}

Further Reading

前面的章节通过常见场景的示例介绍了 Spring Security 对 OAuth2的支持。您可以在参考文档的下列部分中阅读有关 OAuth2 Client 和 Resource Server 的更多信息: