在 Oauth2 安全(客户端凭据)资源服务器应用程序中获取 403 禁止 WebFluxTest
Getting 403 Forbidden for WebFluxTest in Oauth2 Secured (Client Credentials) Resource Server Application
我有一个反应式(Spring WebFlux)网络应用程序,其中我只有很少的 REST APIs 是受保护的资源。(Oauth2)。要手动访问它们,我需要获取具有客户端凭据授予类型的授权令牌并在请求中使用该令牌。
现在,我需要编写测试,我可以通过 Spring 的 WebTestClient 进行调用来调用 API。我在尝试访问 API 时收到 403 禁止。我在写测试用例的时候哪里做错了
以下是我的安全配置:
@EnableWebFluxSecurity
public class WebSecurityConfiguration {
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeExchange()
.pathMatchers(ACTUATOR_ENDPOINT_PATTERN)
.permitAll()
.pathMatchers("/my/api/*")
.hasAuthority("SCOPE_myApi")
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
http.addFilterAfter(new SomeFilter(), SecurityWebFiltersOrder.AUTHORIZATION);
return http.build();
}
@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
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder().filter(oauth).build();
}
}
注意:- 我需要这个 webclient bean,因为在那个过滤器(我添加到 SecurityWebFilterChain)中我正在调用另一个受保护的 resource/API 和那个 API 的响应正在反应上下文中设置
我的应用程序 yaml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${oidc-issuer-uri}
client:
provider:
myProvider:
issuer-uri: ${oidc-issuer-uri}
registration:
myProvider:
client-id: another-service-client
client-secret: ${another-service-clientSecret}
scope: anotherServiceScope
authorization-grant-type: client_credentials
我的控制器:
@RestController
public class MyController {
@GetMapping(value = "/my/api/greet")
public Mono<String> greet() {
return Mono.subscriberContext()
.flatMap(context -> {
String someVal = context.get("MY_CONTEXT"); //This context is being set inside the filter 'SomeFilter'
//Use this someVal
return Mono.just("Hello World");
});
}
}
我的测试用例:
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = {MyController.class})
@Import({WebSecurityConfiguration.class})
@WithMockUser
public class MyControllerTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void test_greet() throws Exception {
webTestClient.mutateWith(csrf()).get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk();
}
}
注意:- 我无法通过不使用我的 WebSecurityConfiguration class 来绕过。因为在网络安全配置中添加的过滤器中设置了反应上下文。
这里需要 2 个东西:
- 首先访问 /my/api/greet,webTestClient 需要 SCOPE_myApi,因为这里不涉及“用户”,所以我们不需要 @WithMockUser
@Test
public void test_greet() {
webTestClient
.mutateWith(mockOidcLogin().authorities(new SimpleGrantedAuthority("SCOPE_myApi")))
.get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("mockSasToken");
}
- 接下来我们需要一个 wiremock 服务器来模拟“另一个服务”的响应
这个选项是使用 spring boot @AutoConfigureWireMock(port = 0) 来自动启动 wiremock 服务器并在随机端口为我们关闭。
接下来我们在测试方法中存根“另一个服务”和 Oauth2 令牌端点的响应。
最后,我们需要一个“测试”spring 配置文件和一个相应的应用程序 -test.yaml 我们告诉 spring 使用 wiremock 端点来获取令牌:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:${wiremock.server.port}/.well-known/jwks_uri
client:
provider:
myProvider:
token-uri: http://localhost:${wiremock.server.port}/.well-known/token
registration:
myProvider:
client-id: mockClient
client-secret: mockSecret
我有一个反应式(Spring WebFlux)网络应用程序,其中我只有很少的 REST APIs 是受保护的资源。(Oauth2)。要手动访问它们,我需要获取具有客户端凭据授予类型的授权令牌并在请求中使用该令牌。
现在,我需要编写测试,我可以通过 Spring 的 WebTestClient 进行调用来调用 API。我在尝试访问 API 时收到 403 禁止。我在写测试用例的时候哪里做错了
以下是我的安全配置:
@EnableWebFluxSecurity
public class WebSecurityConfiguration {
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeExchange()
.pathMatchers(ACTUATOR_ENDPOINT_PATTERN)
.permitAll()
.pathMatchers("/my/api/*")
.hasAuthority("SCOPE_myApi")
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
http.addFilterAfter(new SomeFilter(), SecurityWebFiltersOrder.AUTHORIZATION);
return http.build();
}
@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
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder().filter(oauth).build();
}
}
注意:- 我需要这个 webclient bean,因为在那个过滤器(我添加到 SecurityWebFilterChain)中我正在调用另一个受保护的 resource/API 和那个 API 的响应正在反应上下文中设置
我的应用程序 yaml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${oidc-issuer-uri}
client:
provider:
myProvider:
issuer-uri: ${oidc-issuer-uri}
registration:
myProvider:
client-id: another-service-client
client-secret: ${another-service-clientSecret}
scope: anotherServiceScope
authorization-grant-type: client_credentials
我的控制器:
@RestController
public class MyController {
@GetMapping(value = "/my/api/greet")
public Mono<String> greet() {
return Mono.subscriberContext()
.flatMap(context -> {
String someVal = context.get("MY_CONTEXT"); //This context is being set inside the filter 'SomeFilter'
//Use this someVal
return Mono.just("Hello World");
});
}
}
我的测试用例:
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = {MyController.class})
@Import({WebSecurityConfiguration.class})
@WithMockUser
public class MyControllerTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void test_greet() throws Exception {
webTestClient.mutateWith(csrf()).get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk();
}
}
注意:- 我无法通过不使用我的 WebSecurityConfiguration class 来绕过。因为在网络安全配置中添加的过滤器中设置了反应上下文。
这里需要 2 个东西:
- 首先访问 /my/api/greet,webTestClient 需要 SCOPE_myApi,因为这里不涉及“用户”,所以我们不需要 @WithMockUser
@Test
public void test_greet() {
webTestClient
.mutateWith(mockOidcLogin().authorities(new SimpleGrantedAuthority("SCOPE_myApi")))
.get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("mockSasToken");
}
- 接下来我们需要一个 wiremock 服务器来模拟“另一个服务”的响应
这个选项是使用 spring boot @AutoConfigureWireMock(port = 0) 来自动启动 wiremock 服务器并在随机端口为我们关闭。
接下来我们在测试方法中存根“另一个服务”和 Oauth2 令牌端点的响应。
最后,我们需要一个“测试”spring 配置文件和一个相应的应用程序 -test.yaml 我们告诉 spring 使用 wiremock 端点来获取令牌:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:${wiremock.server.port}/.well-known/jwks_uri
client:
provider:
myProvider:
token-uri: http://localhost:${wiremock.server.port}/.well-known/token
registration:
myProvider:
client-id: mockClient
client-secret: mockSecret