Authorized Client Features

本部分介绍了 Spring Security 为 OAuth2 客户端提供的其他功能。

This section covers additional features provided by Spring Security for the OAuth2 client.

Resolving an Authorized Client

@RegisteredOAuth2AuthorizedClient 注解提供了将方法参数解析为类型为 OAuth2AuthorizedClient 的参数值的能力。与通过使用 OAuth2AuthorizedClientManagerOAuth2AuthorizedClientService 访问 OAuth2AuthorizedClient 相比,这是十分方便的替代方法。以下示例显示了如何使用 @RegisteredOAuth2AuthorizedClient

The @RegisteredOAuth2AuthorizedClient annotation provides the ability to resolve a method parameter to an argument value of type OAuth2AuthorizedClient. This is a convenient alternative compared to accessing the OAuth2AuthorizedClient by using the OAuth2AuthorizedClientManager or OAuth2AuthorizedClientService. The following example shows how to use @RegisteredOAuth2AuthorizedClient:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@GetMapping("/")
	public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		...

		return "index";
	}
}
@Controller
class OAuth2ClientController {
    @GetMapping("/")
    fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
        val accessToken = authorizedClient.accessToken

        ...

        return "index"
    }
}

@RegisteredOAuth2AuthorizedClient 注解由 OAuth2AuthorizedClientArgumentResolver 处理,它直接使用 OAuth2AuthorizedClientManager,因此继承了它的功能。

The @RegisteredOAuth2AuthorizedClient annotation is handled by OAuth2AuthorizedClientArgumentResolver, which directly uses an OAuth2AuthorizedClientManager and, therefore, inherits its capabilities.

WebClient Integration for Servlet Environments

OAuth 2.0 客户端支持通过使用 xref:servlet/oauth2/client/core#oauth2Client-authorized-manager-provider.adoc.adoc[OAuth2AuthorizedClientManagerWebClient 集成。

The OAuth 2.0 Client support integrates with WebClient by using an ExchangeFilterFunction.

ServletOAuth2AuthorizedClientExchangeFilterFunction 提供使用 OAuth2AuthorizedClient 并包括关联的 OAuth2AccessToken 作为承载令牌来请求受保护资源的机制。它直接使用 OAuth2AuthorizedClientManager,因此继承了以下功能:

The ServletOAuth2AuthorizedClientExchangeFilterFunction provides a mechanism for requesting protected resources by using an OAuth2AuthorizedClient and including the associated OAuth2AccessToken as a Bearer Token. It directly uses an OAuth2AuthorizedClientManager and, therefore, inherits the following capabilities:

  • An OAuth2AccessToken is requested if the client has not yet been authorized.

    • authorization_code: Triggers the Authorization Request redirect to initiate the flow.

    • client_credentials: The access token is obtained directly from the Token Endpoint.

    • password: The access token is obtained directly from the Token Endpoint.

  • If the OAuth2AccessToken is expired, it is refreshed (or renewed) if an OAuth2AuthorizedClientProvider is available to perform the authorization

下面的代码展示了如何将 OAuth2AuthorizedClientManager 与 OAuth 2.0 客户端支持进行配置的一个示例:

The following code shows an example of how to configure WebClient with OAuth 2.0 Client support:

  • Java

  • Kotlin

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
    return WebClient.builder()
            .apply(oauth2Client.oauth2Configuration())
            .build()
}

Providing the Authorized Client

xref:servlet/oauth2/client/core#oauth2Client-authorized-manager-provider.adoc.adoc[OAuth2AuthorizedClientManager 通过从 WebClient (请求属性)解析 ServletOAuth2AuthorizedClientExchangeFilterFunction 来确定要用于(一个请求的)客户端。

The ServletOAuth2AuthorizedClientExchangeFilterFunction determines the client to use (for a request) by resolving the OAuth2AuthorizedClient from the ClientRequest.attributes() (request attributes).

下面的代码展示了如何将 OAuth2AuthorizedClient 设置为请求属性:

The following code shows how to set an OAuth2AuthorizedClient as a request attribute:

  • Java

  • Kotlin

@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
	String resourceUri = ...

	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(oauth2AuthorizedClient(authorizedClient))   1
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
    val resourceUri: String = ...
    val body: String = webClient
            .get()
            .uri(resourceUri)
            .attributes(oauth2AuthorizedClient(authorizedClient)) 1
            .retrieve()
            .bodyToMono()
            .block()

    ...

    return "index"
}
1 oauth2AuthorizedClient() is a static method in ServletOAuth2AuthorizedClientExchangeFilterFunction.

下面的代码展示了如何将 ClientRequest.attributes() 设置为请求属性:

The following code shows how to set the ClientRegistration.getRegistrationId() as a request attribute:

  • Java

  • Kotlin

@GetMapping("/")
public String index() {
	String resourceUri = ...

	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(clientRegistrationId("okta"))   1
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}
@GetMapping("/")
fun index(): String {
    val resourceUri: String = ...

    val body: String = webClient
            .get()
            .uri(resourceUri)
            .attributes(clientRegistrationId("okta"))  1
            .retrieve()
            .bodyToMono()
            .block()

    ...

    return "index"
}
1 clientRegistrationId() is a static method in ServletOAuth2AuthorizedClientExchangeFilterFunction.

下面的代码展示了如何将 OAuth2AuthorizedClient 设置为请求属性:

The following code shows how to set an Authentication as a request attribute:

  • Java

  • Kotlin

@GetMapping("/")
public String index() {
	String resourceUri = ...

	Authentication anonymousAuthentication = new AnonymousAuthenticationToken(
			"anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(authentication(anonymousAuthentication))   1
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}
@GetMapping("/")
fun index(): String {
    val resourceUri: String = ...

    val anonymousAuthentication: Authentication = AnonymousAuthenticationToken(
            "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))
    val body: String = webClient
            .get()
            .uri(resourceUri)
            .attributes(authentication(anonymousAuthentication))  1
            .retrieve()
            .bodyToMono()
            .block()

    ...

    return "index"
}
1 authentication() is a static method in ServletOAuth2AuthorizedClientExchangeFilterFunction.

It is recommended to be cautious with this feature since all HTTP requests will receive an access token bound to the provided principal.

Defaulting the Authorized Client

如果既没有 ClientRegistration.getRegistrationId() 也未在 Authentication 提供作为请求属性,则 OAuth2AuthorizedClient 可以根据其配置确定要使用的 ClientRegistration.getRegistrationId() 客户端。

If neither OAuth2AuthorizedClient or ClientRegistration.getRegistrationId() is provided as a request attribute, the ServletOAuth2AuthorizedClientExchangeFilterFunction can determine the default client to use, depending on its configuration.

如果 ServletOAuth2AuthorizedClientExchangeFilterFunction 已经配置,并且用户已经使用 default 进行了身份验证,则与当前 setDefaultOAuth2AuthorizedClient(true) 相关联的 HttpSecurity.oauth2Login() 被使用。

If setDefaultOAuth2AuthorizedClient(true) is configured and the user has authenticated by using HttpSecurity.oauth2Login(), the OAuth2AccessToken associated with the current OAuth2AuthenticationToken is used.

下面的代码展示了具体配置:

The following code shows the specific configuration:

  • Java

  • Kotlin

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	oauth2Client.setDefaultOAuth2AuthorizedClient(true);
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
    oauth2Client.setDefaultOAuth2AuthorizedClient(true)
    return WebClient.builder()
            .apply(oauth2Client.oauth2Configuration())
            .build()
}

谨慎使用此功能,因为所有 HTTP 请求都会收到访问令牌。

Be cautious with this feature, since all HTTP requests receive the access token.

或者,如果 OAuth2AccessToken 使用有效的 OAuth2AuthenticationToken 进行配置,则与 setDefaultClientRegistrationId("okta") 相关联的 ClientRegistration 被使用。

Alternatively, if setDefaultClientRegistrationId("okta") is configured with a valid ClientRegistration, the OAuth2AccessToken associated with the OAuth2AuthorizedClient is used.

下面的代码展示了具体配置:

The following code shows the specific configuration:

  • Java

  • Kotlin

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	oauth2Client.setDefaultClientRegistrationId("okta");
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
    oauth2Client.setDefaultClientRegistrationId("okta")
    return WebClient.builder()
            .apply(oauth2Client.oauth2Configuration())
            .build()
}

谨慎使用此功能,因为所有 HTTP 请求都会收到访问令牌。

Be cautious with this feature, since all HTTP requests receive the access token.