Authorization Grant Support

Authorization Code

有关 Authorization Code 授权的更多详细信息,请参考 OAuth 2.0 授权框架。

Please refer to the OAuth 2.0 Authorization Framework for further details on the Authorization Code grant.

Obtaining Authorization

有关授权码授权的 Authorization Request/Response 协议流,请参考。

Please refer to the Authorization Request/Response protocol flow for the Authorization Code grant.

Initiating the Authorization Request

OAuth2AuthorizationRequestRedirectWebFilter 使用 ServerOAuth2AuthorizationRequestResolver 解析 OAuth2AuthorizationRequest 并通过将最终用户用户代理重定向到授权服务器授权端点来启动授权码许可流程。

The OAuth2AuthorizationRequestRedirectWebFilter uses a ServerOAuth2AuthorizationRequestResolver to resolve an OAuth2AuthorizationRequest and initiate the Authorization Code grant flow by redirecting the end-user’s user-agent to the Authorization Server’s Authorization Endpoint.

ServerOAuth2AuthorizationRequestResolver 的主要角色是从提供的 Web 请求中解析一个 OAuth2AuthorizationRequest。默认实现 DefaultServerOAuth2AuthorizationRequestResolver 匹配(默认)路径 /oauth2/authorization/{registrationId},提取 registrationId 并使用它为关联的 ClientRegistration 构建 OAuth2AuthorizationRequest

The primary role of the ServerOAuth2AuthorizationRequestResolver is to resolve an OAuth2AuthorizationRequest from the provided web request. The default implementation DefaultServerOAuth2AuthorizationRequestResolver matches on the (default) path /oauth2/authorization/{registrationId} extracting the registrationId and using it to build the OAuth2AuthorizationRequest for the associated ClientRegistration.

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            scope: read, write
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

具有基本路径 /oauth2/authorization/okta 的请求将通过 OAuth2AuthorizationRequestRedirectWebFilter 启动授权请求重定向,并最终启动授权码许可流程。

A request with the base path /oauth2/authorization/okta will initiate the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectWebFilter and ultimately start the Authorization Code grant flow.

AuthorizationCodeReactiveOAuth2AuthorizedClientProviderReactiveOAuth2AuthorizedClientProvider 的授权码授权实现,它还由 OAuth2AuthorizationRequestRedirectWebFilter 初始化授权请求重定向。

The AuthorizationCodeReactiveOAuth2AuthorizedClientProvider is an implementation of ReactiveOAuth2AuthorizedClientProvider for the Authorization Code grant, which also initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectWebFilter.

如果 OAuth 2.0 客户端是 Public Client,则按如下配置 OAuth 2.0 客户端注册:

If the OAuth 2.0 Client is a Public Client, then configure the OAuth 2.0 Client registration as follows:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: none
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            ...

公共客户端使用 Proof Key for Code Exchange(PKCE)提供支持。如果客户端在不可信环境中运行(例如,本地应用程序或基于 Web 浏览器的应用程序),因此无法维持其证书的机密性,则当满足以下条件时将自动使用 PKCE:

Public Clients are supported using Proof Key for Code Exchange (PKCE). If the client is running in an untrusted environment (eg. native application or web browser-based application) and therefore incapable of maintaining the confidentiality of it’s credentials, PKCE will automatically be used when the following conditions are true:

  1. client-secret is omitted (or empty)

  2. client-authentication-method is set to "none" (ClientAuthenticationMethod.NONE)

如果 OAuth 2.0 提供程序支持 Confidential Clients 的 PKCE,您可以(可选)使用 DefaultServerOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()) 配置它。

If the OAuth 2.0 Provider supports PKCE for Confidential Clients, you may (optionally) configure it using DefaultServerOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()).

DefaultServerOAuth2AuthorizationRequestResolver 可使用 UriComponentsBuilder 也支持 redirect-uriURI 模板变量。

The DefaultServerOAuth2AuthorizationRequestResolver also supports URI template variables for the redirect-uri using UriComponentsBuilder.

以下配置使用所有受支持的 URI 模板变量:

The following configuration uses all the supported URI template variables:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            ...
            redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
            ...

{baseUrl} 解析为 {baseScheme}://{baseHost}{basePort}{basePath}

{baseUrl} resolves to {baseScheme}://{baseHost}{basePort}{basePath}

使用 URI 模板变量配置 redirect-uri 在 OAuth 2.0 Client 在 Proxy Server 后面运行时尤为有用。这样可确保在扩展 redirect-uri 时使用 X-Forwarded-* 头部。

Configuring the redirect-uri with URI template variables is especially useful when the OAuth 2.0 Client is running behind a Proxy Server. This ensures that the X-Forwarded-* headers are used when expanding the redirect-uri.

Customizing the Authorization Request

ServerOAuth2AuthorizationRequestResolver 可以实现的主要用例之一是能够使用 OAuth 2.0 授权框架中定义的标准参数之上的其他参数自定义授权请求。

One of the primary use cases a ServerOAuth2AuthorizationRequestResolver can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.

例如,OpenID Connect 为 Authorization Code Flow 定义额外的 OAuth 2.0 请求参数,该请求参数从 OAuth 2.0 Authorization Framework 中定义的标准参数扩展而来。这些扩展参数之一是 prompt 参数。

For example, OpenID Connect defines additional OAuth 2.0 request parameters for the Authorization Code Flow extending from the standard parameters defined in the OAuth 2.0 Authorization Framework. One of those extended parameters is the prompt parameter.

可选的。用空格分隔,区分大小写的 ASCII 字符串值列表,用于指定授权服务器是否提示最终用户重新进行身份验证并同意。定义的值为: 无、登录、同意、选择账户

OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are: none, login, consent, select_account

以下示例演示了如何使用 Consumer<OAuth2AuthorizationRequest.Builder> 配置 DefaultServerOAuth2AuthorizationRequestResolver,该 Consumer<OAuth2AuthorizationRequest.Builder> 通过包含请求参数 prompt=consent 来自定义 oauth2Login() 的授权请求。

The following example shows how to configure the DefaultServerOAuth2AuthorizationRequestResolver with a Consumer<OAuth2AuthorizationRequest.Builder> that customizes the Authorization Request for oauth2Login(), by including the request parameter prompt=consent.

  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2LoginSecurityConfig {

	@Autowired
	private ReactiveClientRegistrationRepository clientRegistrationRepository;

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.authorizeExchange(authorize -> authorize
				.anyExchange().authenticated()
			)
			.oauth2Login(oauth2 -> oauth2
				.authorizationRequestResolver(
					authorizationRequestResolver(this.clientRegistrationRepository)
				)
			);
		return http.build();
	}

	private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(
			ReactiveClientRegistrationRepository clientRegistrationRepository) {

		DefaultServerOAuth2AuthorizationRequestResolver authorizationRequestResolver =
				new DefaultServerOAuth2AuthorizationRequestResolver(
						clientRegistrationRepository);
		authorizationRequestResolver.setAuthorizationRequestCustomizer(
				authorizationRequestCustomizer());

		return  authorizationRequestResolver;
	}

	private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
		return customizer -> customizer
					.additionalParameters(params -> params.put("prompt", "consent"));
	}
}
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {

    @Autowired
    private lateinit var customClientRegistrationRepository: ReactiveClientRegistrationRepository

    @Bean
    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            authorizeExchange {
                authorize(anyExchange, authenticated)
            }
            oauth2Login {
                authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
            }
        }

        return http.build()
    }

    private fun authorizationRequestResolver(
            clientRegistrationRepository: ReactiveClientRegistrationRepository): ServerOAuth2AuthorizationRequestResolver {
        val authorizationRequestResolver = DefaultServerOAuth2AuthorizationRequestResolver(
                clientRegistrationRepository)
        authorizationRequestResolver.setAuthorizationRequestCustomizer(
                authorizationRequestCustomizer())
        return authorizationRequestResolver
    }

    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
        return Consumer { customizer ->
            customizer
                .additionalParameters { params -> params["prompt"] = "consent" }
        }
    }
}

对于简单的用例,如果附加请求参数对于特定提供程序始终相同,则可以将其直接添加到 authorization-uri 属性中。

For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the authorization-uri property.

例如,如果请求参数 prompt 的值对于提供程序 okta 始终为 consent,则只需照如下进行配置:

For example, if the value for the request parameter prompt is always consent for the provider okta, than simply configure as follows:

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent

上述示例演示了在标准参数之上添加自定义参数的常见用例。或者,如果您的要求更高级,您可以通过简单地覆盖 OAuth2AuthorizationRequest.authorizationRequestUri 属性来完全控制生成授权请求 URI。

The preceding example shows the common use case of adding a custom parameter on top of the standard parameters. Alternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by simply overriding the OAuth2AuthorizationRequest.authorizationRequestUri property.

OAuth2AuthorizationRequest.Builder.build() 构建了 OAuth2AuthorizationRequest.authorizationRequestUri,它表示使用 application/x-www-form-urlencoded 格式的所有查询参数的授权请求 URI。

OAuth2AuthorizationRequest.Builder.build() constructs the OAuth2AuthorizationRequest.authorizationRequestUri, which represents the Authorization Request URI including all query parameters using the application/x-www-form-urlencoded format.

以下示例演示了上一示例中 authorizationRequestCustomizer() 的一个变体,并且覆盖了 OAuth2AuthorizationRequest.authorizationRequestUri 属性。

The following example shows a variation of authorizationRequestCustomizer() from the preceding example, and instead overrides the OAuth2AuthorizationRequest.authorizationRequestUri property.

  • Java

  • Kotlin

private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
	return customizer -> customizer
			.authorizationRequestUri(uriBuilder -> uriBuilder
					.queryParam("prompt", "consent").build());
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
        customizer
                .authorizationRequestUri { uriBuilder: UriBuilder ->
                    uriBuilder
                            .queryParam("prompt", "consent").build()
                }
    }
}

Storing the Authorization Request

ServerAuthorizationRequestRepository 负责从发起授权请求到收到授权响应(回调)这段时间的 OAuth2AuthorizationRequest 的持久性。

The ServerAuthorizationRequestRepository is responsible for the persistence of the OAuth2AuthorizationRequest from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).

OAuth2AuthorizationRequest 用于关联和验证授权响应。

The OAuth2AuthorizationRequest is used to correlate and validate the Authorization Response.

ServerAuthorizationRequestRepository 的默认实现是 WebSessionOAuth2ServerAuthorizationRequestRepository,它将 OAuth2AuthorizationRequest 存储在 WebSession 中。

The default implementation of ServerAuthorizationRequestRepository is WebSessionOAuth2ServerAuthorizationRequestRepository, which stores the OAuth2AuthorizationRequest in the WebSession.

如果您有 ServerAuthorizationRequestRepository 的自定义实现,则可以按以下示例所示进行配置:

If you have a custom implementation of ServerAuthorizationRequestRepository, you may configure it as shown in the following example:

ServerAuthorizationRequestRepository Configuration
  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.oauth2Client(oauth2 -> oauth2
				.authorizationRequestRepository(this.authorizationRequestRepository())
				...
			);
		return http.build();
	}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            oauth2Client {
                authorizationRequestRepository = authorizationRequestRepository()
            }
        }

        return http.build()
    }
}

Requesting an Access Token

有关授权码授权的 Access Token Request/Response 协议流,请参考。

Please refer to the Access Token Request/Response protocol flow for the Authorization Code grant.

用于授权代码授予的 ReactiveOAuth2AccessTokenResponseClient 的默认实现是 WebClientReactiveAuthorizationCodeTokenResponseClient,它使用 WebClient 在授权服务器令牌端点处将授权代码兑换为访问令牌。

The default implementation of ReactiveOAuth2AccessTokenResponseClient for the Authorization Code grant is WebClientReactiveAuthorizationCodeTokenResponseClient, which uses a WebClient for exchanging an authorization code for an access token at the Authorization Server’s Token Endpoint.

WebClientReactiveAuthorizationCodeTokenResponseClient 非常灵活,因为它允许您自定义令牌请求的预处理和/或令牌响应的后处理。

The WebClientReactiveAuthorizationCodeTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义的 Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>`向 `WebClientReactiveAuthorizationCodeTokenResponseClient.setParametersConverter()`提供支持。默认实现构建 `MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 Access Token Requestgrant_type`参数,该参数用于构建请求。授权代码授予所需的其它参数由 `WebClientReactiveAuthorizationCodeTokenResponseClient`直接添加到请求正文中。不过,提供自定义的 `Converter,这能让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide WebClientReactiveAuthorizationCodeTokenResponseClient.setParametersConverter() with a custom Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>. The default implementation builds a MultiValueMap<String, String> containing only the grant_type parameter of a standard OAuth 2.0 Access Token Request which is used to construct the request. Other parameters required by the Authorization Code grant are added directly to the body of the request by the WebClientReactiveAuthorizationCodeTokenResponseClient. However, providing a custom Converter, would allow you to extend the standard Token Request and add custom parameter(s).

如果你只想添加其他参数,则可以用带有自定义 Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>WebClientReactiveAuthorizationCodeTokenResponseClient.addParametersConverter() 代替,它会构建一个聚合 Converter

If you prefer to only add additional parameters, you can instead provide WebClientReactiveAuthorizationCodeTokenResponseClient.addParametersConverter() with a custom Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回目标 OAuth 2.0 提供程序可理解的 OAuth 2.0 访问令牌请求的有效参数。

The custom Converter must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要自定义令牌响应的后处理,则需要为 WebClientReactiveAuthorizationCodeTokenResponseClient.setBodyExtractor() 提供使用自定义配置的 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>,该 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> 用于将 OAuth 2.0 访问令牌响应转换为 OAuth2AccessTokenResponse。由 OAuth2BodyExtractors.oauth2AccessTokenResponse() 提供的默认实现会解析响应并相应地处理错误。

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide WebClientReactiveAuthorizationCodeTokenResponseClient.setBodyExtractor() with a custom configured BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> that is used for converting the OAuth 2.0 Access Token Response to an OAuth2AccessTokenResponse. The default implementation provided by OAuth2BodyExtractors.oauth2AccessTokenResponse() parses the response and handles errors accordingly.

Customizing the WebClient

或者,如果您的要求更高级,您可以通过简单地为 WebClientReactiveAuthorizationCodeTokenResponseClient.setWebClient() 提供使用自定义配置的 WebClient 来完全控制请求/响应。

Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing WebClientReactiveAuthorizationCodeTokenResponseClient.setWebClient() with a custom configured WebClient.

无论是自定义 WebClientReactiveAuthorizationCodeTokenResponseClient 还是提供您自己的 ReactiveOAuth2AccessTokenResponseClient 实现,您都需要按以下示例所示进行配置:

Whether you customize WebClientReactiveAuthorizationCodeTokenResponseClient or provide your own implementation of ReactiveOAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

Access Token Response Configuration
  • Java

  • Kotlin

@Configuration
@EnableWebFluxSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
		http
			.oauth2Client(oauth2 -> oauth2
				.authenticationManager(this.authorizationCodeAuthenticationManager())
				...
			);
		return http.build();
	}

	private ReactiveAuthenticationManager authorizationCodeAuthenticationManager() {
		WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
				new WebClientReactiveAuthorizationCodeTokenResponseClient();
		...

		return new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);
	}
}
@Configuration
@EnableWebFluxSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http {
            oauth2Client {
                authenticationManager = authorizationCodeAuthenticationManager()
            }
        }

        return http.build()
    }

    private fun authorizationCodeAuthenticationManager(): ReactiveAuthenticationManager {
        val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
        ...

        return OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient)
    }
}

Refresh Token

有关 Refresh Token 的更多详细信息,请参考 OAuth 2.0 授权框架。

Please refer to the OAuth 2.0 Authorization Framework for further details on the Refresh Token.

Refreshing an Access Token

有关刷新令牌授权的 Access Token Request/Response 协议流,请参考。

Please refer to the Access Token Request/Response protocol flow for the Refresh Token grant.

用于刷新令牌授予的 ReactiveOAuth2AccessTokenResponseClient 的默认实现是 WebClientReactiveRefreshTokenTokenResponseClient,它在授权服务器令牌端点处刷新访问令牌时使用 WebClient

The default implementation of ReactiveOAuth2AccessTokenResponseClient for the Refresh Token grant is WebClientReactiveRefreshTokenTokenResponseClient, which uses a WebClient when refreshing an access token at the Authorization Server’s Token Endpoint.

WebClientReactiveRefreshTokenTokenResponseClient 非常灵活,因为它允许您自定义令牌请求的预处理和/或令牌响应的后处理。

The WebClientReactiveRefreshTokenTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义的 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>`向 `WebClientReactiveRefreshTokenTokenResponseClient.setParametersConverter()`提供支持。默认实现构建 `MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 Access Token Requestgrant_type`参数,该参数用于构建请求。刷新令牌授予所需的其它参数由 `WebClientReactiveRefreshTokenTokenResponseClient`直接添加到请求正文中。不过,提供自定义的 `Converter,这能让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide WebClientReactiveRefreshTokenTokenResponseClient.setParametersConverter() with a custom Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>. The default implementation builds a MultiValueMap<String, String> containing only the grant_type parameter of a standard OAuth 2.0 Access Token Request which is used to construct the request. Other parameters required by the Refresh Token grant are added directly to the body of the request by the WebClientReactiveRefreshTokenTokenResponseClient. However, providing a custom Converter, would allow you to extend the standard Token Request and add custom parameter(s).

如果你只想添加其他参数,则可以用带有自定义 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>WebClientReactiveRefreshTokenTokenResponseClient.addParametersConverter() 代替,它会构建一个聚合 Converter

If you prefer to only add additional parameters, you can instead provide WebClientReactiveRefreshTokenTokenResponseClient.addParametersConverter() with a custom Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回目标 OAuth 2.0 提供程序可理解的 OAuth 2.0 访问令牌请求的有效参数。

The custom Converter must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要自定义令牌响应的后处理,您将需要为 WebClientReactiveRefreshTokenTokenResponseClient.setBodyExtractor() 提供使用 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> 自定义配置,它用于转换 OAuth 2.0 访问令牌响应到 OAuth2AccessTokenResponse。由 OAuth2BodyExtractors.oauth2AccessTokenResponse() 提供的默认实现解析响应并相应地处理错误。

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide WebClientReactiveRefreshTokenTokenResponseClient.setBodyExtractor() with a custom configured BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> that is used for converting the OAuth 2.0 Access Token Response to an OAuth2AccessTokenResponse. The default implementation provided by OAuth2BodyExtractors.oauth2AccessTokenResponse() parses the response and handles errors accordingly.

Customizing the WebClient

或者,如果您有更高级的需求,您可以通过简单地为 WebClientReactiveRefreshTokenTokenResponseClient.setWebClient() 提供使用 WebClient 自定义配置来完全控制请求/响应。

Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing WebClientReactiveRefreshTokenTokenResponseClient.setWebClient() with a custom configured WebClient.

无论您是自定义 WebClientReactiveRefreshTokenTokenResponseClient 还是提供您自己的 ReactiveOAuth2AccessTokenResponseClient 的实现,都需要按照以下示例进行配置。

Whether you customize WebClientReactiveRefreshTokenTokenResponseClient or provide your own implementation of ReactiveOAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

Access Token Response Configuration
  • Java

  • Kotlin

// Customize
ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
		ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val refreshTokenTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...

val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
        .authorizationCode()
        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken() 配置一个 RefreshTokenReactiveOAuth2AuthorizedClientProvider,它是一个 ReactiveOAuth2AuthorizedClientProvider 的实现,用于刷新令牌授予。

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken() configures a RefreshTokenReactiveOAuth2AuthorizedClientProvider, which is an implementation of a ReactiveOAuth2AuthorizedClientProvider for the Refresh Token grant.

对于 authorization_codepassword 授权类型,可以在访问令牌响应中选择性返回 OAuth2RefreshToken。如果 OAuth2AuthorizedClient.getRefreshToken() 可用且 OAuth2AuthorizedClient.getAccessToken() 已过期,它将由 RefreshTokenReactiveOAuth2AuthorizedClientProvider 自动刷新。

The OAuth2RefreshToken may optionally be returned in the Access Token Response for the authorization_code and password grant types. If the OAuth2AuthorizedClient.getRefreshToken() is available and the OAuth2AuthorizedClient.getAccessToken() is expired, it will automatically be refreshed by the RefreshTokenReactiveOAuth2AuthorizedClientProvider.

Client Credentials

请参阅 OAuth 2.0 授权框架,进一步了解 Client Credentials 授权。

Please refer to the OAuth 2.0 Authorization Framework for further details on the Client Credentials grant.

Requesting an Access Token

有关客户端凭据授权的 Access Token Request/Response 协议流,请参考。

Please refer to the Access Token Request/Response protocol flow for the Client Credentials grant.

用于客户端凭据授权的 ReactiveOAuth2AccessTokenResponseClient 的默认实现是 WebClientReactiveClientCredentialsTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 WebClient

The default implementation of ReactiveOAuth2AccessTokenResponseClient for the Client Credentials grant is WebClientReactiveClientCredentialsTokenResponseClient, which uses a WebClient when requesting an access token at the Authorization Server’s Token Endpoint.

WebClientReactiveClientCredentialsTokenResponseClient 非常灵活,因为它允许您自定义令牌请求的预处理和/或令牌响应的后处理。

The WebClientReactiveClientCredentialsTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义的 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>`向 `WebClientReactiveClientCredentialsTokenResponseClient.setParametersConverter()`提供支持。默认实现构建 `MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 Access Token Requestgrant_type`参数,该参数用于构建请求。客户端凭据授予所需的其它参数由 `WebClientReactiveClientCredentialsTokenResponseClient`直接添加到请求正文中。不过,提供自定义的 `Converter,这能让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide WebClientReactiveClientCredentialsTokenResponseClient.setParametersConverter() with a custom Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>. The default implementation builds a MultiValueMap<String, String> containing only the grant_type parameter of a standard OAuth 2.0 Access Token Request which is used to construct the request. Other parameters required by the Client Credentials grant are added directly to the body of the request by the WebClientReactiveClientCredentialsTokenResponseClient. However, providing a custom Converter, would allow you to extend the standard Token Request and add custom parameter(s).

如果您只想添加其他参数,您可以向 WebClientReactiveClientCredentialsTokenResponseClient.addParametersConverter() 提供自定义 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>,该方法构造一个聚合 Converter

If you prefer to only add additional parameters, you can instead provide WebClientReactiveClientCredentialsTokenResponseClient.addParametersConverter() with a custom Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回目标 OAuth 2.0 提供程序可理解的 OAuth 2.0 访问令牌请求的有效参数。

The custom Converter must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要自定义令牌响应的后处理,您将需要为 WebClientReactiveClientCredentialsTokenResponseClient.setBodyExtractor() 提供使用 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> 自定义配置,它用于转换 OAuth 2.0 访问令牌响应到 OAuth2AccessTokenResponse。由 OAuth2BodyExtractors.oauth2AccessTokenResponse() 提供的默认实现解析响应并相应地处理错误。

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide WebClientReactiveClientCredentialsTokenResponseClient.setBodyExtractor() with a custom configured BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> that is used for converting the OAuth 2.0 Access Token Response to an OAuth2AccessTokenResponse. The default implementation provided by OAuth2BodyExtractors.oauth2AccessTokenResponse() parses the response and handles errors accordingly.

Customizing the WebClient

或者,如果您有更高级的需求,您可以通过简单地为 WebClientReactiveClientCredentialsTokenResponseClient.setWebClient() 提供使用 WebClient 自定义配置来完全控制请求/响应。

Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing WebClientReactiveClientCredentialsTokenResponseClient.setWebClient() with a custom configured WebClient.

无论您自定义 WebClientReactiveClientCredentialsTokenResponseClient 还是提供您自己的 ReactiveOAuth2AccessTokenResponseClient 的实现,都需要按照以下示例进行配置。

Whether you customize WebClientReactiveClientCredentialsTokenResponseClient or provide your own implementation of ReactiveOAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

  • Java

  • Kotlin

// Customize
ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
		ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
				.clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val clientCredentialsTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...

val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() 配置了一个 ClientCredentialsReactiveOAuth2AuthorizedClientProvider,它是 ReactiveOAuth2AuthorizedClientProvider 在客户端凭据授予中的实现。

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() configures a ClientCredentialsReactiveOAuth2AuthorizedClientProvider, which is an implementation of a ReactiveOAuth2AuthorizedClientProvider for the Client Credentials grant.

Using the Access Token

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: client_credentials
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​以及 ReactiveOAuth2AuthorizedClientManager @Bean

…​and the ReactiveOAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

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

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.clientCredentials()
					.build();

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

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

可按如下方式获取 OAuth2AccessToken

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attribute(ServerWebExchange.class.getName(), exchange)
				.build();

		return this.authorizedClientManager.authorize(authorizeRequest)
				.map(OAuth2AuthorizedClient::getAccessToken)
				...
				.thenReturn("index");
	}
}
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attribute(ServerWebExchange::class.java.name, exchange)
                .build()

        return authorizedClientManager.authorize(authorizeRequest)
                .map { it.accessToken }
                ...
                .thenReturn("index")
    }
}

ServerWebExchange 是一个可选属性。如果不是提供的,它将通过键 ServerWebExchange.classReactor’s Context 中获取。

ServerWebExchange is an OPTIONAL attribute. If not provided, it will be obtained from the Reactor’s Context via the key ServerWebExchange.class.

Resource Owner Password Credentials

有关 Resource Owner Password Credentials 授权的更多详细信息,请参考 OAuth 2.0 授权框架。

Please refer to the OAuth 2.0 Authorization Framework for further details on the Resource Owner Password Credentials grant.

Requesting an Access Token

有关资源拥有者密码凭据授权的 Access Token Request/Response 协议流,请参考。

Please refer to the Access Token Request/Response protocol flow for the Resource Owner Password Credentials grant.

用于资源所有者密码凭据授权的 ReactiveOAuth2AccessTokenResponseClient 的默认实现是 WebClientReactivePasswordTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 WebClient

The default implementation of ReactiveOAuth2AccessTokenResponseClient for the Resource Owner Password Credentials grant is WebClientReactivePasswordTokenResponseClient, which uses a WebClient when requesting an access token at the Authorization Server’s Token Endpoint.

WebClientReactivePasswordTokenResponseClient 非常灵活,因为它允许您自定义令牌请求的预处理和/或令牌响应的后处理。

The WebClientReactivePasswordTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义的 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>`向 `WebClientReactivePasswordTokenResponseClient.setParametersConverter()`提供支持。默认实现构建 `MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 Access Token Requestgrant_type`参数,该参数用于构建请求。资源所有者密码凭据授予所需的其它参数由 `WebClientReactivePasswordTokenResponseClient`直接添加到请求正文中。不过,提供自定义的 `Converter,这能让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide WebClientReactivePasswordTokenResponseClient.setParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>. The default implementation builds a MultiValueMap<String, String> containing only the grant_type parameter of a standard OAuth 2.0 Access Token Request which is used to construct the request. Other parameters required by the Resource Owner Password Credentials grant are added directly to the body of the request by the WebClientReactivePasswordTokenResponseClient. However, providing a custom Converter, would allow you to extend the standard Token Request and add custom parameter(s).

如果您只想添加其他参数,您可以向 WebClientReactivePasswordTokenResponseClient.addParametersConverter() 提供自定义 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>,该方法构造一个聚合 Converter

If you prefer to only add additional parameters, you can instead provide WebClientReactivePasswordTokenResponseClient.addParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回目标 OAuth 2.0 提供程序可理解的 OAuth 2.0 访问令牌请求的有效参数。

The custom Converter must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要自定义令牌响应的后处理,您将需要为 WebClientReactivePasswordTokenResponseClient.setBodyExtractor() 提供使用 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> 自定义配置,它用于转换 OAuth 2.0 访问令牌响应到 OAuth2AccessTokenResponse。由 OAuth2BodyExtractors.oauth2AccessTokenResponse() 提供的默认实现解析响应并相应地处理错误。

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide WebClientReactivePasswordTokenResponseClient.setBodyExtractor() with a custom configured BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> that is used for converting the OAuth 2.0 Access Token Response to an OAuth2AccessTokenResponse. The default implementation provided by OAuth2BodyExtractors.oauth2AccessTokenResponse() parses the response and handles errors accordingly.

Customizing the WebClient

或者,如果您有更高级的需求,您可以通过简单地为 WebClientReactivePasswordTokenResponseClient.setWebClient() 提供使用 WebClient 自定义配置来完全控制请求/响应。

Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing WebClientReactivePasswordTokenResponseClient.setWebClient() with a custom configured WebClient.

无论您自定义 WebClientReactivePasswordTokenResponseClient 还是提供您自己的 ReactiveOAuth2AccessTokenResponseClient 的实现,都需要按照以下示例进行配置。

Whether you customize WebClientReactivePasswordTokenResponseClient or provide your own implementation of ReactiveOAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

  • Java

  • Kotlin

// Customize
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
		ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
				.password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
				.refreshToken()
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
val passwordTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...

val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
        .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
        .refreshToken()
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password() 配置了一个 PasswordReactiveOAuth2AuthorizedClientProvider,它是 ReactiveOAuth2AuthorizedClientProvider 在资源所有者密码凭据授予中的实现。

ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password() configures a PasswordReactiveOAuth2AuthorizedClientProvider, which is an implementation of a ReactiveOAuth2AuthorizedClientProvider for the Resource Owner Password Credentials grant.

Using the Access Token

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: password
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​以及 ReactiveOAuth2AuthorizedClientManager @Bean

…​and the ReactiveOAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

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

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.password()
					.refreshToken()
					.build();

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

	// Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
	// map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
	authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

	return authorizedClientManager;
}

private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
	return authorizeRequest -> {
		Map<String, Object> contextAttributes = Collections.emptyMap();
		ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
		ServerHttpRequest request = exchange.getRequest();
		String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
		String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			contextAttributes = new HashMap<>();

			// `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
			contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
			contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
		}
		return Mono.just(contextAttributes);
	};
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .password()
            .refreshToken()
            .build()
    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

    // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
    // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
    return authorizedClientManager
}

private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
    return Function { authorizeRequest ->
        var contextAttributes: MutableMap<String, Any> = mutableMapOf()
        val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
        val request: ServerHttpRequest = exchange.request
        val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
        val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
        if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
            contextAttributes = hashMapOf()

            // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
            contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
            contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
        }
        Mono.just(contextAttributes)
    }
}

可按如下方式获取 OAuth2AccessToken

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attribute(ServerWebExchange.class.getName(), exchange)
				.build();

		return this.authorizedClientManager.authorize(authorizeRequest)
				.map(OAuth2AuthorizedClient::getAccessToken)
				...
				.thenReturn("index");
	}
}
@Controller
class OAuth2ClientController {
    @Autowired
    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attribute(ServerWebExchange::class.java.name, exchange)
                .build()

        return authorizedClientManager.authorize(authorizeRequest)
                .map { it.accessToken }
                ...
                .thenReturn("index")
    }
}

ServerWebExchange 是一个可选属性。如果不是提供的,它将通过键 ServerWebExchange.classReactor’s Context 中获取。

ServerWebExchange is an OPTIONAL attribute. If not provided, it will be obtained from the Reactor’s Context via the key ServerWebExchange.class.

JWT Bearer

请参阅 JSON Web Token (JWT) OAuth 2.0 客户端认证和认证授予简介以详细了解 JWT Bearer 授权。

Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the JWT Bearer grant.

Requesting an Access Token

请参阅 Access Token Request/Response 协议流程以了解 JWT 持有者授权。

Please refer to the Access Token Request/Response protocol flow for the JWT Bearer grant.

用于 JWT Bearer 授权的 ReactiveOAuth2AccessTokenResponseClient 的默认实现是 WebClientReactiveJwtBearerTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 WebClient

The default implementation of ReactiveOAuth2AccessTokenResponseClient for the JWT Bearer grant is WebClientReactiveJwtBearerTokenResponseClient, which uses a WebClient when requesting an access token at the Authorization Server’s Token Endpoint.

WebClientReactiveJwtBearerTokenResponseClient 非常灵活,因为它允许您自定义令牌请求的预处理和/或令牌响应的后处理。

The WebClientReactiveJwtBearerTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义的 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>`向 `WebClientReactiveJwtBearerTokenResponseClient.setParametersConverter()`提供支持。默认实现构建 `MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 Access Token Requestgrant_type`参数,该参数用于构建请求。JWT 持有者授予所需的其它参数由 `WebClientReactiveJwtBearerTokenResponseClient`直接添加到请求正文中。不过,提供自定义的 `Converter,这能让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide WebClientReactiveJwtBearerTokenResponseClient.setParametersConverter() with a custom Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>. The default implementation builds a MultiValueMap<String, String> containing only the grant_type parameter of a standard OAuth 2.0 Access Token Request which is used to construct the request. Other parameters required by the JWT Bearer grant are added directly to the body of the request by the WebClientReactiveJwtBearerTokenResponseClient. However, providing a custom Converter, would allow you to extend the standard Token Request and add custom parameter(s).

如果您只想添加其他参数,您可以向 WebClientReactiveJwtBearerTokenResponseClient.addParametersConverter() 提供自定义 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>,该方法构造一个聚合 Converter

If you prefer to only add additional parameters, you can instead provide WebClientReactiveJwtBearerTokenResponseClient.addParametersConverter() with a custom Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回目标 OAuth 2.0 提供程序可理解的 OAuth 2.0 访问令牌请求的有效参数。

The custom Converter must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要定制令牌响应的后处理,您需要向 WebClientReactiveJwtBearerTokenResponseClient.setBodyExtractor() 提供一个定制配置的 BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>,该配置用于将 OAuth 2.0 访问令牌响应转换为 OAuth2AccessTokenResponse。默认实现由 OAuth2BodyExtractors.oauth2AccessTokenResponse() 提供,解析响应并相应地处理错误。

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide WebClientReactiveJwtBearerTokenResponseClient.setBodyExtractor() with a custom configured BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> that is used for converting the OAuth 2.0 Access Token Response to an OAuth2AccessTokenResponse. The default implementation provided by OAuth2BodyExtractors.oauth2AccessTokenResponse() parses the response and handles errors accordingly.

Customizing the WebClient

或者,如果您的要求更高级,您可以通过简单地向 WebClientReactiveJwtBearerTokenResponseClient.setWebClient() 提供一个定制配置的 WebClient 来完全控制请求/响应。

Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing WebClientReactiveJwtBearerTokenResponseClient.setWebClient() with a custom configured WebClient.

无论您是要定制 WebClientReactiveJwtBearerTokenResponseClient 还是提供自己实现的 ReactiveOAuth2AccessTokenResponseClient,您都需要按以下示例所示进行配置:

Whether you customize WebClientReactiveJwtBearerTokenResponseClient or provide your own implementation of ReactiveOAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

  • Java

  • Kotlin

// Customize
ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...

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

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
		ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
				.provider(jwtBearerAuthorizedClientProvider)
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val jwtBearerTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...

val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)

val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
        .provider(jwtBearerAuthorizedClientProvider)
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

Using the Access Token

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​和 OAuth2AuthorizedClientManager @Bean

…​and the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

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

	JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
			new JwtBearerReactiveOAuth2AuthorizedClientProvider();

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.provider(jwtBearerAuthorizedClientProvider)
					.build();

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

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
    val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .provider(jwtBearerAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

可按如下方式获取 OAuth2AccessToken

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public Mono<String> resource(JwtAuthenticationToken jwtAuthentication, ServerWebExchange exchange) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();

		return this.authorizedClientManager.authorize(authorizeRequest)
				.map(OAuth2AuthorizedClient::getAccessToken)
				...
	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken, exchange: ServerWebExchange): Mono<String> {
        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        return authorizedClientManager.authorize(authorizeRequest)
                .map { it.accessToken }
                ...
    }
}

JwtBearerReactiveOAuth2AuthorizedClientProvider 默认情况下通过 OAuth2AuthorizationContext.getPrincipal().getPrincipal() 解析 Jwt 断言,因此在前面的示例中使用了 JwtAuthenticationToken

JwtBearerReactiveOAuth2AuthorizedClientProvider resolves the Jwt assertion via OAuth2AuthorizationContext.getPrincipal().getPrincipal() by default, hence the use of JwtAuthenticationToken in the preceding example.

如果您需要从不同来源解析 Jwt 断言,您可以向 JwtBearerReactiveOAuth2AuthorizedClientProvider.setJwtAssertionResolver() 提供自定义 Function<OAuth2AuthorizationContext, Mono<Jwt>>

If you need to resolve the Jwt assertion from a different source, you can provide JwtBearerReactiveOAuth2AuthorizedClientProvider.setJwtAssertionResolver() with a custom Function<OAuth2AuthorizationContext, Mono<Jwt>>.