【发布时间】:2020-11-30 19:05:37
【问题描述】:
我正在尝试使用 Spring Security 5 实现 oauth2 安全 Spring Boot API。我希望 API 成为 oauth2 资源服务器,并且能够使用 WebClient 访问外部 oauth2 资源服务器,并授予客户端凭据。
我可以将 API 配置为 oauth2 资源服务器或 oauth2 客户端,但不能同时配置两者。
以下是将 API 配置为具有 Spring 安全性 5 的资源服务器的最小设置。我使用的是不透明令牌,因此为此配置了服务器。
application.properties
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8086/auth/oauth/check_token
spring.security.oauth2.resourceserver.opaquetoken.client-id=test-api
spring.security.oauth2.resourceserver.opaquetoken.client-secret=e61aa5d6-074d-4216-b15f-1bf3fc71b833
WebSecurity 配置类
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
}
}
通过此设置,我可以使用有效令牌访问此 api 端点以访问其受保护的资源。所以资源服务器配置本身就可以正常工作。
以下是配置为使用 WebClient 使用客户端凭据授予访问外部受保护资源的最小 Spring security 5 设置。
application.properties
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=http://127.0.0.1:8086/auth/oauth/token
spring.security.oauth2.client.registration.test-api.client-id=test-store
spring.security.oauth2.client.registration.test-api.client-secret=password
spring.security.oauth2.client.registration.test-api.provider=my-oauth-provider
spring.security.oauth2.client.registration.test-api.scope=read,write
spring.security.oauth2.client.registration.test-api.authorization-grant-type=client_credentials
WebSecurity 配置类
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
ClientRegistrationRepository clientRegistrationRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client(oauth2 -> oauth2
.clientRegistrationRepository(clientRegistrationRepository)
);
}
}
Oauth2Client 配置类
@Configuration
public class Oauth2ClientConfig {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(
oAuth2AuthorizedClientManager);
// default registrationId
oauth2Client.setDefaultClientRegistrationId("test-api");
// set client to use oauth2 by default globally
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
}
通过此设置,我可以使用客户端凭据授权访问外部受保护端点以访问受保护资源。
但是,当我将这两个配置放在一起时,当我尝试访问我的 api 端点时它不起作用,而后者又尝试使用 WebClient 访问外部资源。
@Autowired
WebClient webClient;
@GetMapping("test")
public String test() {
String message = webClient
.get()
.uri("http://localhost:8084/external/api/endpoint")
.retrieve()
.bodyToMono(String.class)
.block();
return message;
}
Spring security 抛出 principalName cannot be empty 错误。
java.lang.IllegalArgumentException: principalName cannot be empty
at org.springframework.util.Assert.hasText(Assert.java:287) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to GET http://localhost:8084/ocr/document/test [DefaultWebClient]
Stack trace:
at org.springframework.util.Assert.hasText(Assert.java:287) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService.loadAuthorizedClient(InMemoryOAuth2AuthorizedClientService.java:73) ~[spring-security-oauth2-client-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository.loadAuthorizedClient(AuthenticatedPrincipalOAuth2AuthorizedClientRepository.java:73) ~[spring-security-oauth2-client-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:144) ~[spring-security-oauth2-client-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient$24(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:534) ~[spring-security-oauth2-client-5.3.3.RELEASE.jar:5.3.3.RELEASE]
...
非常感谢任何有关解决此问题的建议。当 API 也是资源服务器时,我应该以其他方式配置 WebClient 以使其工作吗?
我的 maven 项目中有这些依赖项。 Spring 安全版本是 5.3.3.RELEASE pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<!-- Spring Boot Web starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Security Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Oauth2 resource server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
</dependency>
<!-- Oauth2 client -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<!-- WebClient -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
【问题讨论】:
标签: spring-boot spring-security oauth-2.0 spring-security-oauth2 spring-webclient