概述
当没有明确的资源所有者或资源所有者与客户无法区分时该怎么办?这是一个相当普遍的情况。例如,当需要在后端系统之间进行直接通信。本文将介绍OAuth2.0客户凭证授权。
oauth2.0文档描述客户凭证授予:
客户使用客户凭证赠款类型在用户的上下文之外获取访问令牌。客户通常被客户使用来访问有关自己的资源,而不是访问用户的资源。
在本文中,您将了解有关具有春季安全性的OAuth2客户凭证赠款,从而允许服务无需认证的用户就可以安全地进行互操作。
oauth2客户端凭证授权比授权代码授权更为简单,通常用于CRON任务和其他类型的后端数据处理等操作。
客户凭证赠款流
当应用程序请求访问令牌以访问其他资源时,使用客户端凭据,而不是代表用户进行授权。
请求参数
Grant_Typeï¼必需¼
必须将grant_type
参数设置为client_credentials
。
Scopeï¼可选
您的服务可以支持客户凭证赠款的不同范围。
客户端验证设施唯一必需¼
客户需要对此请求进行身份验证。通常,该服务将允许其他请求参数client_id
和client_secret
,或在HTTP Basic Auth标题中接受客户ID和秘密。
ðâ€注意:如果您不想阅读直到最后,您可以在此处查看source code。
OAuth2授权服务器
在这里,我们使用Spring Authorization Server构建OAuth2授权服务器。我不会在这里重复细节。您可以参考文章Using JWT with Spring Security OAuth2来构建授权服务器。这里仅描述了与以前的授权代码授权授权服务配置的区别。
配置
当我们使用注册的构建器类型创建客户端时,我们将配置客户端以支持客户凭证授权并简单地将其存储在内存中。
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("relive-client")
.clientSecret("{noop}relive-client")
.clientAuthenticationMethods(s -> {
s.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
s.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
})
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8070/login/oauth2/code/messaging-client-model")
.scope("message.read")
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(true)
.requireProofKey(false)
.build())
.tokenSettings(TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
.accessTokenTimeToLive(Duration.ofSeconds(30 * 60))
.refreshTokenTimeToLive(Duration.ofSeconds(60 * 60))
.reuseRefreshTokens(true)
.build())
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
上面我们配置了一个OAuth2客户端并指定授权granttype 作为 client_credentials :
- clientid :重新定位
- clientsecret :重新定位
- redirecturi :http://127.0.0.1:8070/login/oauth2/code/messaging-client-model
- 范围:message.read
使用Spring Security构建OAuth2资源服务器
OAuth2资源服务器配置与本文Using JWT with Spring Security OAuth2中的资源服务设置一致,您可以在本文中参考OAuth2资源服务的介绍,或在本文末尾获得本文的源代码地址文章要查看。
配置
OAuth2资源服务器提供/resource/actits 受保护的端点,并使用Spring Security来保护此服务。
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/resource/article")
.and()
.authorizeRequests()
.mvcMatchers("/resource/article")
.access("hasAuthority('SCOPE_message.read')")
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
请注意,oauth2资源服务/resource/actits endpoint需要“邮件。是“ message.read”而不是“ scope_message.read”。
使用Spring Security构建OAuth2客户端
在本节中,您将使用当前推荐的WebClient请求资源服务,这是Spring的WebFlux软件包的一部分。这是春季的反应性,非阻滞API,您可以在Spring Documentation中阅读有关它的更多信息。
在@Scheduled
定义的cron任务下,您将使用WebClient
提出请求。
Maven依赖性
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>1.0.9</version>
</dependency>
配置
我们将在application.yml
中配置OAuth2授权信息,并指定OAuth2客户端服务端口号:
server:
port: 8070
spring:
security:
oauth2:
client:
registration:
messaging-client-model:
provider: client-provider
client-id: relive-client
client-secret: relive-client
authorization-grant-type: client_credentials
client-authentication-method: client_secret_post
scope: message.read
client-name: messaging-client-model
provider:
client-provider:
token-uri: http://127.0.0.1:8080/oauth2/token
接下来,我们将创建一个SecurityConfig
类,以配置Spring Security Oauth2客户端所需的bean:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().permitAll()
)
.oauth2Client(withDefaults());
return http.build();
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(oauth2Client)
.build();
}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder
.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
我们创建一个WebClient实例,以对资源服务器执行HTTP请求,并将OAuth2授权过滤器添加到WebClient。 AuthorizedClientServiceOAuth2AuthorizedClientManager
这是协调OAuth2客户凭证请求的高级控制器类,在这里我要指出,AuthorizedClientServiceOAuth2AuthorizedClientManager
是专门设计的类,专门用于在Httpservletrequest的上下文之外使用。
DefaultoAuth2AuthorizedClientManager旨在用于HttpservletRequest的上下文。在HttpservletRequest的上下文外操作时,请改用授权ClientserviceoAuth2AuthorizedClientManager。
接下来,我们将创建一个使用@Scheduled
注释定义的任务,并注入网络定位来调用资源服务请求:
@Service
public class ArticleJob {
@Autowired
private WebClient webClient;
@Scheduled(cron = "0/2 * * * * ? ")
public void exchange() {
List list = this.webClient
.get()
.uri("http://127.0.0.1:8090/resource/article")
.attributes(clientRegistrationId("messaging-client-model"))
.retrieve()
.bodyToMono(List.class)
.block();
log.info("Call resource server execution result:" + list);
}
}
客户服务将每2秒发出一次请求,并在控制台上打印结果。
结论
一如既往,本文中使用的源代码可用on GitHub。
感谢您的阅读!