此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.5! |
OAuth2
Spring Security 提供全面的 OAuth 2.0 支持。 本节讨论如何将 OAuth 2.0 集成到基于 servlet 的应用程序中。
概述
Spring Security 的 OAuth 2.0 支持包括两个主要功能集:
OAuth2 登录是一个非常强大的 OAuth2 客户端功能,值得在参考文档中单独使用一节。 但是,它不作为独立功能存在,需要 OAuth2 客户端才能运行。 |
这些功能集涵盖了 OAuth 2.0 授权框架中定义的资源服务器和客户端角色,而授权服务器角色由 Spring 授权服务器涵盖,Spring Authorization Server 是一个基于 Spring Security 构建的独立项目。
OAuth2 中的资源服务器和客户端角色通常由一个或多个服务器端应用程序表示。 此外,授权服务器角色可以由一个或多个第三方表示(就像在组织内集中身份管理和/或身份验证时一样)-或者-它可以由应用程序表示(就像 Spring Authorization Server 的情况一样)。
例如,典型的基于 OAuth2 的微服务架构可能由一个面向用户的客户端应用程序、多个提供 REST API 的后端资源服务器和一个用于管理用户和身份验证问题的第三方授权服务器组成。 同样常见的情况是,单个应用程序仅代表其中一个角色,并且需要与提供其他角色的一个或多个第三方集成。
Spring Security 可以处理这些场景以及更多场景。 以下部分介绍了 Spring Security 提供的角色,并包含常见场景的示例。
OAuth2 资源服务器
本节包含 OAuth2 Resource Server 功能的摘要和示例。 请参阅 OAuth 2.0 Resource Server 以获取完整的参考文档。 |
要开始使用,请添加spring-security-oauth2-resource-server
依赖项。
使用 Spring Boot 时,添加以下 starter:
-
Gradle
-
Maven
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
有关不使用 Spring Boot 时的其他选项,请参见获取 Spring Security。 |
请考虑以下 OAuth2 Resource Server 的使用案例:
-
我想使用 OAuth2 保护对 API 的访问(授权服务器提供 JWT 或不透明访问令牌)
-
我想使用 JWT(自定义令牌)保护对 API 的访问
使用 OAuth2 访问令牌保护访问权限
使用 OAuth2 访问令牌保护对 API 的访问是很常见的。 在大多数情况下, Spring Security 只需要最少的配置即可使用 OAuth2 保护应用程序。
有两种类型的Bearer
Spring Security 支持的令牌,每个令牌使用不同的组件进行验证:
JWT 支持
以下示例将JwtDecoder
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://my-auth-server.com
使用 Spring Boot 时,这就是所需的全部内容。 Spring Boot 提供的默认安排等效于以下内容:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromIssuerLocation("https://my-auth-server.com");
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt { }
}
}
return http.build()
}
@Bean
fun jwtDecoder(): JwtDecoder {
return JwtDecoders.fromIssuerLocation("https://my-auth-server.com")
}
}
不透明令牌支持
以下示例将OpaqueTokenIntrospector
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
resourceserver:
opaquetoken:
introspection-uri: https://my-auth-server.com/oauth2/introspect
client-id: my-client-id
client-secret: my-client-secret
使用 Spring Boot 时,这就是所需的全部内容。 Spring Boot 提供的默认安排等效于以下内容:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.opaqueToken(Customizer.withDefaults())
);
return http.build();
}
@Bean
public OpaqueTokenIntrospector opaqueTokenIntrospector() {
return new SpringOpaqueTokenIntrospector(
"https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret");
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
opaqueToken { }
}
}
return http.build()
}
@Bean
fun opaqueTokenIntrospector(): OpaqueTokenIntrospector {
return SpringOpaqueTokenIntrospector(
"https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret"
)
}
}
使用自定义 JWT 保护 Access
使用 JWT 保护对 API 的访问是一个相当常见的目标,尤其是当前端开发为单页应用程序时。
Spring Security 中的 OAuth2 资源服务器支持可用于任何类型的Bearer
令牌,包括自定义 JWT。
使用 JWT 保护 API 所需要做的只是JwtDecoder
bean,用于验证签名和解码令牌。
Spring Security 将自动使用提供的 bean 在SecurityFilterChain
.
以下示例将JwtDecoder
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
resourceserver:
jwt:
public-key-location: classpath:my-public-key.pub
您可以将公钥作为类路径资源(称为 |
使用 Spring Boot 时,这就是所需的全部内容。 Spring Boot 提供的默认安排等效于以下内容:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(publicKey()).build();
}
private RSAPublicKey publicKey() {
// ...
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt { }
}
}
return http.build()
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withPublicKey(publicKey()).build()
}
private fun publicKey(): RSAPublicKey {
// ...
}
}
Spring Security 不提供用于铸造代币的端点。
但是,Spring Security 确实提供了 |
OAuth2 客户端
本节包含 OAuth2 客户端功能摘要和示例。 请参阅 OAuth 2.0 客户端 和 OAuth 2.0 登录 以获取完整的参考文档。 |
要开始使用,请添加spring-security-oauth2-client
依赖项。
使用 Spring Boot 时,添加以下 starter:
-
Gradle
-
Maven
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
有关不使用 Spring Boot 时的其他选项,请参见获取 Spring Security。 |
请考虑 OAuth2 客户端的以下用例:
-
我想用
RestClient
获取用户的 Access Token为了访问第三方 API -
我想用
WebClient
获取用户的 Access Token为了访问第三方 API -
我想同时执行这两项作(登录用户并访问第三方 API)
-
我想使用
client_credentials
授权类型为每个应用程序获取一个令牌 -
我想启用扩展授权类型
使用 OAuth2 登录用户
要求用户通过 OAuth2 登录是很常见的。OpenID Connect 1.0 提供了一个称为id_token
,旨在为 OAuth2 客户端提供执行用户身份验证和登录用户的能力。
在某些情况下,OAuth2 可以直接用于登录用户(例如,GitHub 和 Facebook 等不实施 OpenID Connect 的流行社交登录提供商就是这种情况)。
以下示例将应用程序配置为充当 OAuth2 客户端,能够使用 OAuth2 或 OpenID Connect 将用户登录:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.oauth2Login(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
oauth2Login { }
}
return http.build()
}
}
除了上述配置外,应用程序至少需要一个ClientRegistration
通过使用ClientRegistrationRepository
豆。
以下示例将InMemoryClientRegistrationRepository
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
client:
registration:
my-oidc-client:
provider: my-oidc-provider
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: openid,profile
provider:
my-oidc-provider:
issuer-uri: https://my-oidc-provider.com
通过上述配置,应用程序现在支持两个额外的终端节点:
-
登录端点(例如
/oauth2/authorization/my-oidc-client
) 用于启动登录并执行到第三方授权服务器的重定向。 -
重定向端点(例如
/login/oauth2/code/my-oidc-client
) 被授权服务器重定向回客户端应用程序,并将包含一个code
参数用于获取id_token
和/或access_token
通过 Access Token 请求。
存在 |
访问受保护的资源
向受 OAuth2 保护的第三方 API 发出请求是 OAuth2 客户端的核心用例。
这是通过授权客户端(由OAuth2AuthorizedClient
类)并通过将Bearer
token 中Authorization
标头。
以下示例将应用程序配置为能够从第三方 API 请求受保护资源的 OAuth2 客户端:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.oauth2Client(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
oauth2Client { }
}
return http.build()
}
}
上面的示例没有提供登录用户的方法。
您可以使用任何其他登录机制(例如 |
除了上述配置外,应用程序至少需要一个ClientRegistration
通过使用ClientRegistrationRepository
豆。
以下示例将InMemoryClientRegistrationRepository
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
client:
registration:
my-oauth2-client:
provider: my-auth-server
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: message.read,message.write
provider:
my-auth-server:
issuer-uri: https://my-auth-server.com
除了配置 Spring Security 以支持 OAuth2 客户端功能之外,您还需要决定如何访问受保护的资源并相应地配置您的应用程序。
Spring Security 提供了OAuth2AuthorizedClientManager
用于获取可用于访问受保护资源的访问令牌。
Spring Security 注册一个默认的 |
使用OAuth2AuthorizedClientManager
是通过ClientHttpRequestInterceptor
它通过RestClient
,该 API 在spring-web
位于 Classpath 上。
以下示例使用默认的OAuth2AuthorizedClientManager
要配置RestClient
能够通过以下方式访问受保护的资源Bearer
令牌Authorization
标头:
RestClient
跟ClientHttpRequestInterceptor
-
Java
-
Kotlin
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
OAuth2ClientHttpRequestInterceptor requestInterceptor =
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build();
}
}
@Configuration
class RestClientConfig {
@Bean
fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build()
}
}
此配置RestClient
可以像以下示例中一样使用:
RestClient
访问受保护的资源-
Java
-
Kotlin
import static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;
@RestController
public class MessagesController {
private final RestClient restClient;
public MessagesController(RestClient restClient) {
this.restClient = restClient;
}
@GetMapping("/messages")
public ResponseEntity<List<Message>> messages() {
Message[] messages = this.restClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.retrieve()
.body(Message[].class);
return ResponseEntity.ok(Arrays.asList(messages));
}
public record Message(String message) {
}
}
import org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId
import org.springframework.web.client.body
@RestController
class MessagesController(private val restClient: RestClient) {
@GetMapping("/messages")
fun messages(): ResponseEntity<List<Message>> {
val messages = restClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.retrieve()
.body<Array<Message>>()!!
.toList()
return ResponseEntity.ok(messages)
}
data class Message(val message: String)
}
访问受保护的资源WebClient
向受 OAuth2 保护的第三方 API 发出请求是 OAuth2 客户端的核心用例。
这是通过授权客户端(由OAuth2AuthorizedClient
类)并通过将Bearer
token 中Authorization
标头。
以下示例将应用程序配置为能够从第三方 API 请求受保护资源的 OAuth2 客户端:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.oauth2Client(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
oauth2Client { }
}
return http.build()
}
}
上面的示例没有提供登录用户的方法。
您可以使用任何其他登录机制(例如 |
除了上述配置外,应用程序至少需要一个ClientRegistration
通过使用ClientRegistrationRepository
豆。
以下示例将InMemoryClientRegistrationRepository
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
client:
registration:
my-oauth2-client:
provider: my-auth-server
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: message.read,message.write
provider:
my-auth-server:
issuer-uri: https://my-auth-server.com
除了配置 Spring Security 以支持 OAuth2 客户端功能之外,您还需要决定如何访问受保护的资源并相应地配置您的应用程序。
Spring Security 提供了OAuth2AuthorizedClientManager
用于获取可用于访问受保护资源的访问令牌。
Spring Security 注册一个默认的 |
而不是配置RestClient
,另一种使用OAuth2AuthorizedClientManager
是通过ExchangeFilterFunction
它通过WebClient
.
要使用WebClient
,您需要添加spring-webflux
dependency 以及响应式客户端实现:
-
Gradle
-
Maven
implementation 'org.springframework:spring-webflux'
implementation 'io.projectreactor.netty:reactor-netty'
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
</dependency>
以下示例使用默认的OAuth2AuthorizedClientManager
要配置WebClient
能够通过以下方式访问受保护的资源Bearer
令牌Authorization
标头:
WebClient
跟ExchangeFilterFunction
-
Java
-
Kotlin
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction filter =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(filter.oauth2Configuration())
.build();
}
}
@Configuration
class WebClientConfig {
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient {
val filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
return WebClient.builder()
.apply(filter.oauth2Configuration())
.build()
}
}
此配置WebClient
可以像以下示例中一样使用:
WebClient
访问受保护的资源-
Java
-
Kotlin
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
@RestController
public class MessagesController {
private final WebClient webClient;
public MessagesController(WebClient webClient) {
this.webClient = webClient;
}
@GetMapping("/messages")
public ResponseEntity<List<Message>> messages() {
return this.webClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.retrieve()
.toEntityList(Message.class)
.block();
}
public record Message(String message) {
}
}
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId
@RestController
class MessagesController(private val webClient: WebClient) {
@GetMapping("/messages")
fun messages(): ResponseEntity<List<Message>> {
return webClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.retrieve()
.toEntityList<Message>()
.block()!!
}
data class Message(val message: String)
}
访问当前用户的受保护资源
当用户通过 OAuth2 或 OpenID Connect 登录时,授权服务器可能会提供可直接用于访问受保护资源的访问令牌。
这很方便,因为它只需要一个ClientRegistration
同时为两个使用案例进行配置。
本部分将 Log Users In with OAuth2 和 Access Protected Resources 合并到一个配置中。
存在其他高级方案,例如配置一个 |
以下示例将应用程序配置为充当 OAuth2 客户端,该客户端能够使用户登录并从第三方 API 请求受保护的资源:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.oauth2Login(Customizer.withDefaults())
.oauth2Client(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
oauth2Login { }
oauth2Client { }
}
return http.build()
}
}
除了上述配置外,应用程序至少需要一个ClientRegistration
通过使用ClientRegistrationRepository
豆。
以下示例将InMemoryClientRegistrationRepository
使用 Spring Boot 配置属性的 bean:
spring:
security:
oauth2:
client:
registration:
my-combined-client:
provider: my-auth-server
client-id: my-client-id
client-secret: my-client-secret
authorization-grant-type: authorization_code
scope: openid,profile,message.read,message.write
provider:
my-auth-server:
issuer-uri: https://my-auth-server.com
前面的示例(使用 OAuth2 登录用户、访问受保护的资源)和此示例之间的主要区别在于通过 |
除了配置 Spring Security 以支持 OAuth2 客户端功能之外,您还需要决定如何访问受保护的资源并相应地配置您的应用程序。
Spring Security 提供了OAuth2AuthorizedClientManager
用于获取可用于访问受保护资源的访问令牌。
Spring Security 注册一个默认的 |
使用OAuth2AuthorizedClientManager
是通过ClientHttpRequestInterceptor
它通过RestClient
,该 API 在spring-web
位于 Classpath 上。
以下示例使用默认的OAuth2AuthorizedClientManager
要配置RestClient
能够通过以下方式访问受保护的资源Bearer
令牌Authorization
标头:
RestClient
跟ClientHttpRequestInterceptor
-
Java
-
Kotlin
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
OAuth2ClientHttpRequestInterceptor requestInterceptor =
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
requestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver());
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build();
}
private static ClientRegistrationIdResolver clientRegistrationIdResolver() {
return (request) -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return (authentication instanceof OAuth2AuthenticationToken principal)
? principal.getAuthorizedClientRegistrationId()
: null;
};
}
}
@Configuration
class RestClientConfig {
@Bean
fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
requestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver())
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build()
}
private fun clientRegistrationIdResolver(): OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver {
return OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver { request ->
val authentication = SecurityContextHolder.getContext().authentication
if (authentication is OAuth2AuthenticationToken) {
authentication.authorizedClientRegistrationId
} else {
null
}
}
}
}
此配置RestClient
可以像以下示例中一样使用:
RestClient
访问受保护的资源(当前用户)-
Java
-
Kotlin
@RestController
public class MessagesController {
private final RestClient restClient;
public MessagesController(RestClient restClient) {
this.restClient = restClient;
}
@GetMapping("/messages")
public ResponseEntity<List<Message>> messages() {
Message[] messages = this.restClient.get()
.uri("http://localhost:8090/messages")
.retrieve()
.body(Message[].class);
return ResponseEntity.ok(Arrays.asList(messages));
}
public record Message(String message) {
}
}
import org.springframework.web.client.body
@RestController
class MessagesController(private val restClient: RestClient) {
@GetMapping("/messages")
fun messages(): ResponseEntity<List<Message>> {
val messages = restClient.get()
.uri("http://localhost:8090/messages")
.retrieve()
.body<Array<Message>>()!!
.toList()
return ResponseEntity.ok(messages)
}
data class Message(val message: String)
}
与前面的示例不同,请注意,我们不需要告诉 Spring Security 有关 |
使用客户端凭证授予
本节重点介绍客户端凭证授权类型的其他注意事项。 请参阅访问受保护的资源,了解所有授权类型的常规设置和使用。 |
客户端凭证授予允许客户端获取access_token
代表自身。
客户端凭证授予是一个简单的流程,不涉及资源所有者(即用户)。
请务必注意,客户端凭据授予的典型使用意味着任何请求(或用户)都可能获取访问令牌并向资源服务器发出受保护的资源请求。 在设计应用程序时要小心,以确保用户无法发出未经授权的请求,因为每个请求都能够获得访问令牌。 |
在用户可以登录的 Web 应用程序中获取访问令牌时,Spring Security 的默认行为是为每个用户获取一个访问令牌。
默认情况下,访问令牌的范围限定为当前用户的主体名称,这意味着每个用户都将收到唯一的访问令牌。 |
使用客户端凭据授予的客户端通常需要将访问令牌的范围限定为应用程序,而不是单个用户,因此每个应用程序只有一个访问令牌。
为了将访问令牌的范围限定为应用程序,您需要设置解析自定义主体名称的策略。
以下示例通过配置RestClient
使用RequestAttributePrincipalResolver
:
RestClient
为client_credentials
-
Java
-
Kotlin
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
OAuth2ClientHttpRequestInterceptor requestInterceptor =
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
requestInterceptor.setPrincipalResolver(new RequestAttributePrincipalResolver());
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build();
}
}
@Configuration
class RestClientConfig {
@Bean
fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
requestInterceptor.setPrincipalResolver(RequestAttributePrincipalResolver())
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build()
}
}
完成上述配置后,可以为每个请求指定一个委托人名称。 以下示例演示如何通过指定主体名称将访问令牌的范围限定为应用程序:
-
Java
-
Kotlin
import static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;
import static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;
@RestController
public class MessagesController {
private final RestClient restClient;
public MessagesController(RestClient restClient) {
this.restClient = restClient;
}
@GetMapping("/messages")
public ResponseEntity<List<Message>> messages() {
Message[] messages = this.restClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.attributes(principal("my-application"))
.retrieve()
.body(Message[].class);
return ResponseEntity.ok(Arrays.asList(messages));
}
public record Message(String message) {
}
}
import org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId
import org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal
import org.springframework.web.client.body
@RestController
class MessagesController(private val restClient: RestClient) {
@GetMapping("/messages")
fun messages(): ResponseEntity<List<Message>> {
val messages = restClient.get()
.uri("http://localhost:8090/messages")
.attributes(clientRegistrationId("my-oauth2-client"))
.attributes(principal("my-application"))
.retrieve()
.body<Array<Message>>()!!
.toList()
return ResponseEntity.ok(messages)
}
data class Message(val message: String)
}
如上例所示,通过 attributes 指定主体名称时,将只有一个访问令牌,它将用于所有请求。 |
启用扩展授权类型
一个常见的使用案例涉及启用和/或配置扩展授权类型。
例如,Spring Security 为jwt-bearer
和token-exchange
grant 类型,但默认情况下不会启用它们,因为它们不是核心 OAuth 2.0 规范的一部分。
使用 Spring Security 6.2 及更高版本,我们可以简单地为一个或多个发布一个 beanOAuth2AuthorizedClientProvider
他们将自动被选取。
以下示例仅启用jwt-bearer
资助类型:
jwt-bearer
授权类型-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AuthorizedClientProvider jwtBearer() {
return new JwtBearerOAuth2AuthorizedClientProvider();
}
}
@Configuration
class SecurityConfig {
@Bean
fun jwtBearer(): OAuth2AuthorizedClientProvider {
return JwtBearerOAuth2AuthorizedClientProvider()
}
}
默认的OAuth2AuthorizedClientManager
将由 Spring Security 在尚未提供时自动发布。
任何自定义 |
为了在 Spring Security 6.2 之前实现上述配置,我们必须自己发布这个 bean,并确保我们也重新启用了默认授权类型。 要了解幕后配置的内容,以下是配置可能的样子:
jwt-bearer
授权类型(6.2 之前的版本)-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(new JwtBearerOAuth2AuthorizedClientProvider())
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
@Configuration
class SecurityConfig {
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository
): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(JwtBearerOAuth2AuthorizedClientProvider())
.build()
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository
)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
}
自定义现有授权类型
通过发布 bean 来启用扩展授权类型的功能还提供了自定义现有授权类型的机会,而无需重新定义默认值。
例如,如果我们想自定义OAuth2AuthorizedClientProvider
对于client_credentials
grant,我们可以简单地发布一个 bean,如下所示:
-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AuthorizedClientProvider clientCredentials() {
ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider =
new ClientCredentialsOAuth2AuthorizedClientProvider();
authorizedClientProvider.setClockSkew(Duration.ofMinutes(5));
return authorizedClientProvider;
}
}
@Configuration
class SecurityConfig {
@Bean
fun clientCredentials(): OAuth2AuthorizedClientProvider {
val authorizedClientProvider = ClientCredentialsOAuth2AuthorizedClientProvider()
authorizedClientProvider.setClockSkew(Duration.ofMinutes(5))
return authorizedClientProvider
}
}
自定义 Token 请求参数
在获取访问令牌时,需要自定义请求参数是相当普遍的。
例如,假设我们想添加一个自定义audience
参数添加到令牌请求中,因为提供程序需要此参数来执行authorization_code
授予。
使用 Spring Security 6.2 及更高版本,我们可以简单地发布OAuth2AccessTokenResponseClient
替换为泛型类型OAuth2AuthorizationCodeGrantRequest
Spring Security 将使用它来配置 OAuth2 客户端组件。
以下示例自定义authorization_code
grant 来获得 DSL 的 API 请求:
-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(parametersConverter());
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
return accessTokenResponseClient;
}
private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
return (grantRequest) -> {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set("audience", "xyz_value");
return parameters;
};
}
}
@Configuration
class SecurityConfig {
@Bean
fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter()
requestEntityConverter.addParametersConverter(parametersConverter())
val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter)
return accessTokenResponseClient
}
private fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {
return Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> { grantRequest ->
LinkedMultiValueMap<String, String>().also { parameters ->
parameters["audience"] = "xyz_value"
}
}
}
}
请注意,我们不需要自定义 |
在 Spring Security 6.2 之前,我们必须确保此自定义应用于 OAuth2 登录(如果我们使用此功能)和使用 Spring Security DSL 的 OAuth2 客户端组件。 要了解幕后配置的内容,以下是配置可能的样子:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(parametersConverter());
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2Login((oauth2Login) -> oauth2Login
.tokenEndpoint((tokenEndpoint) -> tokenEndpoint
.accessTokenResponseClient(accessTokenResponseClient)
)
)
.oauth2Client((oauth2Client) -> oauth2Client
.authorizationCodeGrant((authorizationCode) -> authorizationCode
.accessTokenResponseClient(accessTokenResponseClient)
)
);
return http.build();
}
private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
// ...
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter()
requestEntityConverter.addParametersConverter(parametersConverter())
val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
tokenResponseClient.setRequestEntityConverter(requestEntityConverter)
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
oauth2Login {
tokenEndpoint {
accessTokenResponseClient = tokenResponseClient
}
}
oauth2Client {
authorizationCodeGrant {
accessTokenResponseClient = tokenResponseClient
}
}
}
return http.build()
}
private fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {
// ...
}
}
对于其他资助类型,我们可以发布其他OAuth2AccessTokenResponseClient
bean 覆盖默认值。
例如,要自定义client_credentials
grant 我们可以发布以下 bean:
-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(parametersConverter());
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
return accessTokenResponseClient;
}
private static Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter() {
// ...
}
}
@Configuration
class SecurityConfig {
@Bean
fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
val requestEntityConverter = OAuth2ClientCredentialsGrantRequestEntityConverter()
requestEntityConverter.addParametersConverter(parametersConverter())
val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter)
return accessTokenResponseClient
}
private fun parametersConverter(): Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> {
// ...
}
}
Spring Security 会自动解析以下泛型类型的OAuth2AccessTokenResponseClient
豆:
-
OAuth2AuthorizationCodeGrantRequest
(参见DefaultAuthorizationCodeTokenResponseClient
) -
OAuth2RefreshTokenGrantRequest
(参见DefaultRefreshTokenTokenResponseClient
) -
OAuth2ClientCredentialsGrantRequest
(参见DefaultClientCredentialsTokenResponseClient
) -
OAuth2PasswordGrantRequest
(参见DefaultPasswordTokenResponseClient
) -
JwtBearerGrantRequest
(参见DefaultJwtBearerTokenResponseClient
) -
TokenExchangeGrantRequest
(参见DefaultTokenExchangeTokenResponseClient
)
发布 |
发布 |
自定义RestOperations
由 OAuth2 客户端组件使用
另一个常见的用例是需要自定义RestOperations
在获取 Access Token 时使用。
我们可能需要执行此作来自定义响应的处理(通过自定义HttpMessageConverter
)或为公司网络应用代理设置(通过自定义的ClientHttpRequestFactory
).
使用 Spring Security 6.2 及更高版本,我们可以简单地发布 bean 类型的OAuth2AccessTokenResponseClient
Spring Security 将配置并发布OAuth2AuthorizedClientManager
Bean 的 Bean 的 Bean 的 Bean
以下示例自定义RestOperations
对于所有受支持的授权类型:
RestOperations
对于 OAuth2 客户端-
Java
-
Kotlin
@Configuration
public class SecurityConfig {
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
DefaultRefreshTokenTokenResponseClient accessTokenResponseClient =
new DefaultRefreshTokenTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordAccessTokenResponseClient() {
DefaultPasswordTokenResponseClient accessTokenResponseClient =
new DefaultPasswordTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
DefaultJwtBearerTokenResponseClient accessTokenResponseClient =
new DefaultJwtBearerTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {
DefaultTokenExchangeTokenResponseClient accessTokenResponseClient =
new DefaultTokenExchangeTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
return accessTokenResponseClient;
}
@Bean
public RestTemplate restTemplate() {
// ...
}
}
@Configuration
class SecurityConfig {
@Bean
fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun refreshTokenAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {
val accessTokenResponseClient = DefaultRefreshTokenTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun passwordAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> {
val accessTokenResponseClient = DefaultPasswordTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun jwtBearerAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {
val accessTokenResponseClient = DefaultJwtBearerTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun tokenExchangeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {
val accessTokenResponseClient = DefaultTokenExchangeTokenResponseClient()
accessTokenResponseClient.setRestOperations(restTemplate())
return accessTokenResponseClient
}
@Bean
fun restTemplate(): RestTemplate {
// ...
}
}
默认的OAuth2AuthorizedClientManager
将由 Spring Security 在尚未提供时自动发布。
请注意,我们不需要自定义 |
在 Spring Security 6.2 之前,我们必须确保将此自定义应用于 OAuth2 登录(如果我们使用此功能)和 OAuth2 客户端组件。
我们必须使用 Spring Security DSL(对于authorization_code
grant) 并发布一个OAuth2AuthorizedClientManager
对于其他授权类型。
要了解幕后配置的内容,以下是配置可能的样子:
RestOperations
对于 OAuth2 客户端(6.2 之前版本)-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate());
http
// ...
.oauth2Login((oauth2Login) -> oauth2Login
.tokenEndpoint((tokenEndpoint) -> tokenEndpoint
.accessTokenResponseClient(accessTokenResponseClient)
)
)
.oauth2Client((oauth2Client) -> oauth2Client
.authorizationCodeGrant((authorizationCode) -> authorizationCode
.accessTokenResponseClient(accessTokenResponseClient)
)
);
return http.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
DefaultRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =
new DefaultRefreshTokenTokenResponseClient();
refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate());
DefaultClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate());
DefaultPasswordTokenResponseClient passwordAccessTokenResponseClient =
new DefaultPasswordTokenResponseClient();
passwordAccessTokenResponseClient.setRestOperations(restTemplate());
DefaultJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =
new DefaultJwtBearerTokenResponseClient();
jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate());
JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
new JwtBearerOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient);
DefaultTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =
new DefaultTokenExchangeTokenResponseClient();
tokenExchangeAccessTokenResponseClient.setRestOperations(restTemplate());
TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
new TokenExchangeOAuth2AuthorizedClientProvider();
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken((refreshToken) -> refreshToken
.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
)
.clientCredentials((clientCredentials) -> clientCredentials
.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
)
.password((password) -> password
.accessTokenResponseClient(passwordAccessTokenResponseClient)
)
.provider(jwtBearerAuthorizedClientProvider)
.provider(tokenExchangeAuthorizedClientProvider)
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
public RestTemplate restTemplate() {
// ...
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
tokenResponseClient.setRestOperations(restTemplate())
http {
// ...
oauth2Login {
tokenEndpoint {
accessTokenResponseClient = tokenResponseClient
}
}
oauth2Client {
authorizationCodeGrant {
accessTokenResponseClient = tokenResponseClient
}
}
}
return http.build()
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository?,
authorizedClientRepository: OAuth2AuthorizedClientRepository?
): OAuth2AuthorizedClientManager {
val refreshTokenAccessTokenResponseClient = DefaultRefreshTokenTokenResponseClient()
refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate())
val clientCredentialsAccessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate())
val passwordAccessTokenResponseClient = DefaultPasswordTokenResponseClient()
passwordAccessTokenResponseClient.setRestOperations(restTemplate())
val jwtBearerAccessTokenResponseClient = DefaultJwtBearerTokenResponseClient()
jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate())
val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient)
val tokenExchangeAccessTokenResponseClient = DefaultTokenExchangeTokenResponseClient()
tokenExchangeAccessTokenResponseClient.setRestOperations(restTemplate())
val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient)
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken { refreshToken ->
refreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
}
.clientCredentials { clientCredentials ->
clientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
}
.password { password ->
password.accessTokenResponseClient(passwordAccessTokenResponseClient)
}
.provider(jwtBearerAuthorizedClientProvider)
.provider(tokenExchangeAuthorizedClientProvider)
.build()
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository
)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
@Bean
fun restTemplate(): RestTemplate {
// ...
}
}