Core Interfaces / Classes
ClientRegistration
OAuth2AccessToken
是向 OAuth 2.0 或 OpenID Connect 1.0 提供程序注册的客户端的表示形式。
客户端注册保存了信息,例如客户端 ID、客户端机密、授权授予类型、重定向 URI、范围、授权 URI、令牌 URI 和其他详细信息。
ClientRegistration
及其属性被定义如下:
public final class ClientRegistration {
private String registrationId; 1
private String clientId; 2
private String clientSecret; 3
private ClientAuthenticationMethod clientAuthenticationMethod; 4
private AuthorizationGrantType authorizationGrantType; 5
private String redirectUri; 6
private Set<String> scopes; 7
private ProviderDetails providerDetails;
private String clientName; 8
public class ProviderDetails {
private String authorizationUri; 9
private String tokenUri; 10
private UserInfoEndpoint userInfoEndpoint;
private String jwkSetUri; 11
private String issuerUri; 12
private Map<String, Object> configurationMetadata; 13
public class UserInfoEndpoint {
private String uri; 14
private AuthenticationMethod authenticationMethod; 15
private String userNameAttributeName; 16
}
}
}
1 | registrationId :唯一标识 ClientRegistration 的 ID。 |
2 | clientId : The client identifier. |
3 | clientSecret : The client secret. |
4 | clientAuthenticationMethod :用于用提供程序验证客户端的方法。支持的值为 client_secret_basic、client_secret_post、private_key_jwt、client_secret_jwt 和 none (public clients)。 |
5 | authorizationGrantType :OAuth 2.0 授权框架定义了四种 Authorization Grant 类型。支持的值为 authorization_code 、client_credentials 、password ,以及扩展授权类型 urn:ietf:params:oauth:grant-type:jwt-bearer 。 |
6 | redirectUri :客户端的已注册重定向 URI,Authorization Server 在最终用户经过身份验证和授权客户端访问权限后会将最终用户的用户代理重定向到该 URI。 |
7 | scopes :在授权请求流期间客户端请求的范围,例如 openid、email 或 profile。 |
8 | clientName :用于客户端的说明性名称。在某些情况下可以使用该名称,例如在自动生成的登录页面中显示客户端的名称时。 |
9 | authorizationUri :授权服务器的授权端点 URI。 |
10 | tokenUri :授权服务器的令牌端点 URI。 |
11 | jwkSetUri :从授权服务器检索 JSON Web Key (JWK) 集中所用的 URI,其中包含用于验证 ID 令牌(以及可选的用户资料响应)的 JSON Web Signature (JWS) 的加密密钥。 |
12 | issuerUri :返回 OpenID Connect 1.0 提供方或 OAuth 2.0 授权服务器的发行人标识符 URI。 |
13 | configurationMetadata : OpenID Provider Configuration Information。如果已配置 Spring Boot 2.x 属性 spring.security.oauth2.client.provider.[providerId].issuerUri ,则此信息才可用。 |
14 | (userInfoEndpoint)uri :用于访问已认证最终用户声明/属性的用户资料端点 URI。 |
15 | (userInfoEndpoint)authenticationMethod :向 UserInfo 终端发送访问令牌时使用的认证方式。受支持的值有 header、form*和 *query。 |
16 | userNameAttributeName :引用最终用户姓名或标识符的 UserInfo 响应中返回的属性名称。 |
可以使用 OpenID Connect 提供程序的 Configuration endpoint或授权服务器的 Metadata endpoint的发现来最初配置 ClientRegistration
。
ClientRegistrations
提供了通过这种方式配置 ClientRegistration
的便捷方法,如以下示例所示:
-
Java
-
Kotlin
ClientRegistration clientRegistration =
ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
以上代码将按顺序查询 https://idp.example.com/issuer/.well-known/openid-configuration
、然后是 https://idp.example.com/.well-known/openid-configuration/issuer
,最后是 https://idp.example.com/.well-known/oauth-authorization-server/issuer
,然后在第一个返回 200 响应时停止。
或者,您可以使用 ClientRegistrations.fromOidcIssuerLocation()
仅查询 OpenID Connect 提供者的配置端点。
ReactiveClientRegistrationRepository
ReactiveClientRegistrationRepository
用作 OAuth 2.0/OpenID Connect 1.0 ClientRegistration
的存储库。
客户端注册信息最终由相关的授权服务器存储和拥有。该存储库提供检索一组主客户端注册信息的功能,该信息与授权服务器一起存储。 |
Spring Boot 2.x 自动配置将 `spring.security.oauth2.client.registration.[registrationId]`下每个属性绑定到 `ClientRegistration`的一个实例,然后在 `ReactiveClientRegistrationRepository`中组合每个 `ClientRegistration`实例。
|
自动配置还将 ReactiveClientRegistrationRepository
注册为 ApplicationContext
中的 @Bean
,以便在应用程序需要时可以使用依赖注入。
下面的清单显示了一个示例:
-
Java
-
Kotlin
@Controller
public class OAuth2ClientController {
@Autowired
private ReactiveClientRegistrationRepository clientRegistrationRepository;
@GetMapping("/")
public Mono<String> index() {
return this.clientRegistrationRepository.findByRegistrationId("okta")
...
.thenReturn("index");
}
}
@Controller
class OAuth2ClientController {
@Autowired
private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository
@GetMapping("/")
fun index(): Mono<String> {
return this.clientRegistrationRepository.findByRegistrationId("okta")
...
.thenReturn("index")
}
}
OAuth2AuthorizedClient
OAuth2AuthorizedClient
是授权客户端的一个表示。当最终用户(资源所有者)授予客户端访问其受保护资源的授权时,客户端被认为是已授权的。
OAuth2AuthorizedClient
的作用是将 OAuth2AccessToken
(和可选的 OAuth2RefreshToken
)关联到 ClientRegistration
(客户端)和资源所有者(即授予授权的 Principal
最终用户)。
ServerOAuth2AuthorizedClientRepository / ReactiveOAuth2AuthorizedClientService
ServerOAuth2AuthorizedClientRepository
负责在 Web 请求之间保留 OAuth2AuthorizedClient
。相对而言,ReactiveOAuth2AuthorizedClientService
的主要角色是管理应用程序级别的 OAuth2AuthorizedClient
。
从开发者的角度来看,ServerOAuth2AuthorizedClientRepository
或 ReactiveOAuth2AuthorizedClientService
提供了查找与一个客户端相关联的 OAuth2AccessToken
的能力,以便可以将其用于发起受保护的资源请求。
下面的清单显示了一个示例:
-
Java
-
Kotlin
@Controller
public class OAuth2ClientController {
@Autowired
private ReactiveOAuth2AuthorizedClientService authorizedClientService;
@GetMapping("/")
public Mono<String> index(Authentication authentication) {
return this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName())
.map(OAuth2AuthorizedClient::getAccessToken)
...
.thenReturn("index");
}
}
@Controller
class OAuth2ClientController {
@Autowired
private lateinit var authorizedClientService: ReactiveOAuth2AuthorizedClientService
@GetMapping("/")
fun index(authentication: Authentication): Mono<String> {
return this.authorizedClientService.loadAuthorizedClient<OAuth2AuthorizedClient>("okta", authentication.name)
.map { it.accessToken }
...
.thenReturn("index")
}
}
Spring Boot 2.x 自动配置在 |
ReactiveOAuth2AuthorizedClientService
的默认实现为 InMemoryReactiveOAuth2AuthorizedClientService
,它在内存中储存 OAuth2AuthorizedClient
。
另外地,R2DBC 实现 R2dbcReactiveOAuth2AuthorizedClientService
还可以被配置为在一个数据库中保留 OAuth2AuthorizedClient
。
|
ReactiveOAuth2AuthorizedClientManager / ReactiveOAuth2AuthorizedClientProvider
ReactiveOAuth2AuthorizedClientManager
负责 OAuth2AuthorizedClient
的整体管理。
主要职责包括:
-
使用 `ReactiveOAuth2AuthorizedClientProvider`对 OAuth 2.0 客户端进行授权(或重新授权)。
-
委派
OAuth2AuthorizedClient`的持久性,通常使用 `ReactiveOAuth2AuthorizedClientService`或 `ServerOAuth2AuthorizedClientRepository
。 -
当 OAuth 2.0 客户端已成功授权(或重新授权)时,委派至
ReactiveOAuth2AuthorizationSuccessHandler
。 -
当 OAuth 2.0 客户端授权(或重新授权)失败时,委派至
ReactiveOAuth2AuthorizationFailureHandler
。
一个 ReactiveOAuth2AuthorizedClientProvider
实现了了一个授权(或重新授权) OAuth 2.0 客户端的策略。这些实现通常会实现一个授权许可类型,例如 authorization_code
、client_credentials
等。
ReactiveOAuth2AuthorizedClientManager
的默认实现为 DefaultReactiveOAuth2AuthorizedClientManager
,它与一个 ReactiveOAuth2AuthorizedClientProvider
关联在一起,该 ReactiveOAuth2AuthorizedClientProvider
可能支持多个授权许可类型,使用基于委托的组合。ReactiveOAuth2AuthorizedClientProviderBuilder
可以在配置和构建基于委托的组合时使用。
下面的代码展示了一个如何配置和构建一个 ReactiveOAuth2AuthorizedClientProvider
组合的示例,该组合为 authorization_code
、refresh_token
、client_credentials
和 password
授权许可类型提供支持:
-
Java
-
Kotlin
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.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()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build()
val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
当一个授权尝试成功时,DefaultReactiveOAuth2AuthorizedClientManager
会委托给 ReactiveOAuth2AuthorizationSuccessHandler
,后者(默认情况下)会通过 ServerOAuth2AuthorizedClientRepository
保存 OAuth2AuthorizedClient
。如果重新授权失败,例如刷新令牌不再有效,则先前保存的 OAuth2AuthorizedClient
会通过 RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler
从 ServerOAuth2AuthorizedClientRepository
中删除。可以用 setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)
和 setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)
自定义默认行为。
DefaultReactiveOAuth2AuthorizedClientManager
也与一个类型为 Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>>
的 contextAttributesMapper
关联,该 contextAttributesMapper
负责将属性从 OAuth2AuthorizeRequest
映射到一个属性 Map
,该属性将与 OAuth2AuthorizationContext
关联。当您需要向 ReactiveOAuth2AuthorizedClientProvider
提供所需(支持的)属性时,这会很有用,例如 PasswordReactiveOAuth2AuthorizedClientProvider
要求资源所有者的 username
和 password
在 OAuth2AuthorizationContext.getAttributes()
中可用。
以下代码显示了 contextAttributesMapper
的示例:
-
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)
}
}
DefaultReactiveOAuth2AuthorizedClientManager
旨在在 ServerWebExchange
的上下文中使用 within。当在 ServerWebExchange
上下文 outside 操作时,请改用 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
。
一个 service application 是何时使用 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
的常见使用案例。服务应用程序通常在后台运行,没有任何用户交互,并且通常在系统级帐户而不是用户帐户下运行。配置了 client_credentials
许可类型的 OAuth 2.0 客户端可以被视为一种服务应用程序。
下面的代码展示了一个如何配置一个为 client_credentials
许可类型提供支持的 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
的示例:
-
Java
-
Kotlin
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ReactiveClientRegistrationRepository,
authorizedClientService: ReactiveOAuth2AuthorizedClientService): ReactiveOAuth2AuthorizedClientManager {
val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build()
val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}