授权授权支持

授权码

有关 授权码 授予的更多详细信息,请参阅 OAuth 2.0 授权框架。

获取授权

有关授权码授予的 授权请求/响应 协议流程,请参阅。

发起授权请求

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

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

给定以下用于 OAuth 2.0 客户端注册的 Spring Boot 属性

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 启动授权请求重定向,并最终启动授权码授予流程。

AuthorizationCodeReactiveOAuth2AuthorizedClientProviderReactiveOAuth2AuthorizedClientProvider 的实现,用于授权码授予,它也通过 OAuth2AuthorizationRequestRedirectWebFilter 启动授权请求重定向。

如果 OAuth 2.0 客户端是 公共客户端,则按如下方式配置 OAuth 2.0 客户端注册

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"
            ...

公共客户端使用 代码交换证明密钥 (PKCE) 支持。如果客户端在不受信任的环境(例如,本机应用程序或基于 Web 浏览器的应用程序)中运行,因此无法维护其凭据的机密性,则在以下条件为真时,将自动使用 PKCE

  1. client-secret 被省略(或为空)

  2. client-authentication-method 设置为“none”(ClientAuthenticationMethod.NONE

如果 OAuth 2.0 提供者支持 机密客户端 的 PKCE,则可以使用 DefaultServerOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce())(可选)对其进行配置。

DefaultServerOAuth2AuthorizationRequestResolver 还支持使用 UriComponentsBuilderredirect-uri 指定 URI 模板变量。

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

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

使用 URI 模板变量配置 redirect-uri 在 OAuth 2.0 客户端运行在 代理服务器 后面时特别有用。这确保在扩展 redirect-uri 时使用 X-Forwarded-* 头部。

自定义授权请求

ServerOAuth2AuthorizationRequestResolver 的主要用例之一是能够使用除 OAuth 2.0 授权框架中定义的标准参数之外的附加参数自定义授权请求。

例如,OpenID Connect 为 授权码流程 定义了额外的 OAuth 2.0 请求参数,这些参数扩展了 OAuth 2.0 授权框架 中定义的标准参数。其中一个扩展参数是 prompt 参数。

prompt 参数是可选的。一个空格分隔的、区分大小写的 ASCII 字符串值列表,指定授权服务器是否提示最终用户重新进行身份验证和授权。定义的值为:noneloginconsentselect_account

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

  • 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 属性中添加它。

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

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

前面的示例展示了在标准参数之上添加自定义参数的常见用例。或者,如果您的需求更高级,您可以通过覆盖OAuth2AuthorizationRequest.authorizationRequestUri属性来完全控制构建授权请求 URI。

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

以下示例展示了authorizationRequestCustomizer()的变体,它来自前面的示例,并覆盖了OAuth2AuthorizationRequest.authorizationRequestUri属性。

  • 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()
                }
    }
}

存储授权请求

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

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

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

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

ServerAuthorizationRequestRepository 配置
  • 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()
    }
}

请求访问令牌

请参考访问令牌请求/响应协议流程,了解授权码授权方式。

ReactiveOAuth2AccessTokenResponseClient的默认实现,用于授权码授权方式,是WebClientReactiveAuthorizationCodeTokenResponseClient,它使用WebClient在授权服务器的令牌端点交换授权码以获取访问令牌。

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以向WebClientReactiveAuthorizationCodeTokenResponseClient.setParametersConverter()提供一个自定义的Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>。默认实现构建一个MultiValueMap<String, String>,其中只包含标准OAuth 2.0 访问令牌请求grant_type参数,该参数用于构建请求。授权码授权方式所需的其它参数直接添加到WebClientReactiveAuthorizationCodeTokenResponseClient的请求主体中。但是,提供一个自定义的Converter,将允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以改为向 WebClientReactiveAuthorizationCodeTokenResponseClient.addParametersConverter() 提供一个自定义的 Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>,它会构建一个聚合的 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

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

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

访问令牌响应配置
  • 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)
    }
}

刷新令牌

有关 刷新令牌 的更多详细信息,请参阅 OAuth 2.0 授权框架。

刷新访问令牌

请参阅 访问令牌请求/响应 协议流程,了解刷新令牌授权。

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

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以向 WebClientReactiveRefreshTokenTokenResponseClient.setParametersConverter() 提供一个自定义的 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>。默认实现会构建一个 MultiValueMap<String, String>,其中只包含标准 OAuth 2.0 访问令牌请求grant_type 参数,该参数用于构建请求。刷新令牌授权所需的其它参数会直接由 WebClientReactiveRefreshTokenTokenResponseClient 添加到请求正文中。但是,提供一个自定义的 Converter 将允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以改为提供 WebClientReactiveRefreshTokenTokenResponseClient.addParametersConverter() 一个自定义的 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>,它会构建一个聚合的 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

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

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

访问令牌响应配置
  • 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 的实现,用于刷新令牌授权。

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

客户端凭据

有关 客户端凭据 授权的更多详细信息,请参阅 OAuth 2.0 授权框架。

请求访问令牌

有关客户端凭据授权的 访问令牌请求/响应 协议流程,请参阅。

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

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以提供一个自定义的 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>WebClientReactiveClientCredentialsTokenResponseClient.setParametersConverter()。默认实现会构建一个 MultiValueMap<String, String>,其中只包含标准 OAuth 2.0 访问令牌请求grant_type 参数,用于构建请求。客户端凭据授权所需的其它参数会直接由 WebClientReactiveClientCredentialsTokenResponseClient 添加到请求体中。但是,提供一个自定义的 Converter 允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以提供一个自定义的 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>WebClientReactiveClientCredentialsTokenResponseClient.addParametersConverter(),它会构建一个聚合的 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

或者,如果您的需求更高级,您可以通过提供一个自定义配置的 WebClientWebClientReactiveClientCredentialsTokenResponseClient.setWebClient() 来完全控制请求/响应。

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

  • 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 的实现,用于客户端凭据授权模式。

使用访问令牌

给定以下用于 OAuth 2.0 客户端注册的 Spring Boot 属性

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

  • 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

  • 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 是一个可选属性。如果未提供,它将从 Reactor 的上下文 中通过键 ServerWebExchange.class 获取。

资源所有者密码凭据

有关 资源所有者密码凭据 授权模式的更多详细信息,请参阅 OAuth 2.0 授权框架。

请求访问令牌

有关资源所有者密码凭据授权模式的 访问令牌请求/响应 协议流程,请参阅。

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

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以使用自定义 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> 提供 WebClientReactivePasswordTokenResponseClient.setParametersConverter()。默认实现构建一个 MultiValueMap<String, String>,其中仅包含标准 OAuth 2.0 访问令牌请求grant_type 参数,该参数用于构建请求。资源所有者密码凭据授权模式所需的其它参数由 WebClientReactivePasswordTokenResponseClient 直接添加到请求正文中。但是,提供自定义 Converter 允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以改为使用自定义 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> 提供 WebClientReactivePasswordTokenResponseClient.addParametersConverter(),它会构建一个聚合 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

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

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

  • 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 的实现,用于资源所有者密码凭据授权模式。

使用访问令牌

给定以下用于 OAuth 2.0 客户端注册的 Spring Boot 属性

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

  • 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

  • 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 是一个可选属性。如果未提供,它将从 Reactor 的上下文 中通过键 ServerWebExchange.class 获取。

JWT 授权

有关 JWT 授权 的更多详细信息,请参阅 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants。

请求访问令牌

有关 JWT 授权的 访问令牌请求/响应 协议流程,请参阅。

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

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以使用自定义 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>WebClientReactiveJwtBearerTokenResponseClient.setParametersConverter() 提供参数。默认实现构建一个 MultiValueMap<String, String>,其中只包含标准 OAuth 2.0 访问令牌请求grant_type 参数,该参数用于构建请求。WebClientReactiveJwtBearerTokenResponseClient 会将 JWT 授权所需的其它参数直接添加到请求体中。但是,提供自定义 Converter 允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以使用自定义 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>WebClientReactiveJwtBearerTokenResponseClient.addParametersConverter() 提供参数,该参数会构建一个聚合 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

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

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

  • 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)

使用访问令牌

给定以下用于 OAuth 2.0 客户端注册的 Spring Boot 属性

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

  • 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

  • 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
如果您需要从其他来源解析 Jwt 断言,您可以向 JwtBearerReactiveOAuth2AuthorizedClientProvider.setJwtAssertionResolver() 提供一个自定义的 Function<OAuth2AuthorizationContext, Mono<Jwt>>

令牌交换

有关 令牌交换 授予的更多详细信息,请参阅 OAuth 2.0 令牌交换。

请求访问令牌

有关令牌交换授予的 令牌交换请求和响应 协议流程,请参阅。

WebClientReactiveTokenExchangeTokenResponseClient 是令牌交换授予的 ReactiveOAuth2AccessTokenResponseClient 的默认实现,它在授权服务器的令牌端点请求访问令牌时使用 WebClient

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

自定义访问令牌请求

如果您需要自定义令牌请求的预处理,您可以向 WebClientReactiveTokenExchangeTokenResponseClient.setParametersConverter() 提供一个自定义的 Converter<TokenExchangeGrantRequest, MultiValueMap<String, String>>。默认实现构建一个 MultiValueMap<String, String>,其中只包含标准 OAuth 2.0 访问令牌请求grant_type 参数,该参数用于构建请求。令牌交换授予所需的其它参数由 WebClientReactiveTokenExchangeTokenResponseClient 直接添加到请求主体中。但是,提供一个自定义的 Converter 将允许您扩展标准令牌请求并添加自定义参数。

如果您只想添加额外的参数,您可以改为向 WebClientReactiveTokenExchangeTokenResponseClient.addParametersConverter() 提供一个自定义的 Converter<TokenExchangeGrantRequest, MultiValueMap<String, String>>,它会构建一个聚合的 Converter
自定义的 Converter 必须返回 OAuth 2.0 访问令牌请求的有效参数,这些参数会被目标 OAuth 2.0 提供者理解。

自定义访问令牌响应

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

自定义 WebClient

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

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

  • Java

  • Kotlin

// Customize
ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...

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

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val tokenExchangeTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...

val tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

使用访问令牌

给定以下用于 OAuth 2.0 客户端注册的 Spring Boot 属性

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:token-exchange
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​以及 OAuth2AuthorizedClientManager @Bean

  • Java

  • Kotlin

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

	TokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
			new TokenExchangeReactiveOAuth2AuthorizedClientProvider();

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

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

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

您可以按如下方式获取 OAuth2AccessToken

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public Mono<String> resource(JwtAuthenticationToken jwtAuthentication) {
		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): Mono<String> {
        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        return authorizedClientManager.authorize(authorizeRequest)
                .map { it.accessToken }
                ...
    }
}
TokenExchangeReactiveOAuth2AuthorizedClientProvider 默认情况下通过 OAuth2AuthorizationContext.getPrincipal().getPrincipal() 解析主题令牌(作为 OAuth2Token),因此在前面的示例中使用了 JwtAuthenticationToken。默认情况下不会解析参与者令牌。
如果您需要从其他来源解析主题令牌,您可以向 TokenExchangeReactiveOAuth2AuthorizedClientProvider.setSubjectTokenResolver() 提供一个自定义的 Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>
如果您需要解析参与者令牌,您可以向 TokenExchangeReactiveOAuth2AuthorizedClientProvider.setActorTokenResolver() 提供一个自定义的 Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>