Vin*_*t F 3 spring-boot spring-webclient
我有一个小型 Spring Boot 2.2 批次,用于写入 OAuth2 REST API。
我已经能够配置以下WebClient
https://medium.com/@asce4s/oauth2-with-spring-webclient-761d16f89cdd并且它按预期工作。
@Configuration
public class MyRemoteServiceClientOauth2Config {
@Bean("myRemoteService")
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("myRemoteService");
return WebClient.builder()
.filter(oauth)
.build();
}
}
Run Code Online (Sandbox Code Playgroud)
但是,现在我想为我的批次编写集成测试,并且我想避免使用“真实”授权服务器来获取令牌:如果外部服务器关闭,我不希望我的测试失败。我希望我的测试是“自主的”。
在我的测试过程中,我调用的远程服务被mockserver
假的服务所取代。
在这种情况下,最佳做法是什么?
@Profile("!test")
并使用 运行我的测试@ActiveProfiles("test")
。我还在测试中导入了测试特定配置: @Configuration
@Profile("test")
public class BatchTestConfiguration {
@Bean("myRemoteService")
public WebClient webClientForTest() {
return WebClient.create();
}
}
Run Code Online (Sandbox Code Playgroud)
但我觉得必须添加@Profile("!test")
我的生产配置并不是很好。
@Primary
在我的 webClientForTest bean 上添加一个,但它不起作用:生产 bean 仍然启用,并且出现异常:没有类型为“org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository”的合格 bean
这是生产bean需要的参数类型
我和你的情况一样,找到了解决办法。首先,为了查看它的实际效果,我创建了一个存储库,其中展示了下面解释的所有内容的实现。
有没有一种“更干净”的方法来替换我正在使用的 WebClient bean,通过调用我的假远程服务而无需先尝试获取令牌的方法?
我不会替换WebClient
您测试中的 bean,而是ReactiveOAuth2AuthorizedClientManager
用模拟替换 bean。为此,您必须稍微修改您的MyRemoteServiceClientOauth2Config
. 您可以通过这种方式配置它,而不是使用现在已弃用的方法UnAuthenticatedServerOAuth2AuthorizedClientRepository
(这也更符合Servlet-Stack 上记录的配置):
@Configuration
public class MyRemoteServiceClientOauth2Config {
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2ClientCredentialsFilter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(reactiveOAuth2AuthorizedClientManager);
oauth2ClientCredentialsFilter.setDefaultClientRegistrationId("myRemoteService");
return WebClient.builder()
.filter(oauth2ClientCredentialsFilter)
.build();
}
@Bean
public ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager(ReactiveClientRegistrationRepository clientRegistrations,
ReactiveOAuth2AuthorizedClientService authorizedClients) {
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
authorizedClientManager.setAuthorizedClientProvider(
new ClientCredentialsReactiveOAuth2AuthorizedClientProvider());
return authorizedClientManager;
}
}
Run Code Online (Sandbox Code Playgroud)
ReactiveOAuth2AuthorizedClientManager
然后你可以创建一个总是返回 aMono
的模拟,OAuth2AuthorizedClient
如下所示:
@TestComponent
@Primary
public class AlwaysAuthorizedOAuth2AuthorizedClientManager implements ReactiveOAuth2AuthorizedClientManager {
@Value("${spring.security.oauth2.client.registration.myRemoteService.client-id}")
String clientId;
@Value("${spring.security.oauth2.client.registration.myRemoteService.client-secret}")
String clientSecret;
@Value("${spring.security.oauth2.client.provider.some-keycloak.token-uri}")
String tokenUri;
/**
* {@inheritDoc}
*
* @return
*/
@Override
public Mono<OAuth2AuthorizedClient> authorize(final OAuth2AuthorizeRequest authorizeRequest) {
return Mono.just(
new OAuth2AuthorizedClient(
ClientRegistration
.withRegistrationId("myRemoteService")
.clientId(clientId)
.clientSecret(clientSecret)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenUri(tokenUri)
.build(),
"some-keycloak",
new OAuth2AccessToken(TokenType.BEARER,
"c29tZS10b2tlbg==",
Instant.now().minus(Duration.ofMinutes(1)),
Instant.now().plus(Duration.ofMinutes(4)))));
}
}
Run Code Online (Sandbox Code Playgroud)
最后@Import
在你的测试中:
@SpringBootTest
@Import(AlwaysAuthorizedOAuth2AuthorizedClientManager.class)
class YourIntegrationTestClass {
// here is your test code
}
Run Code Online (Sandbox Code Playgroud)
对应的src/test/resources/application.yml
看起来像这样:
spring:
security:
oauth2:
client:
registration:
myRemoteService:
authorization-grant-type: client_credentials
client-id: test-client
client-secret: 6b30087f-65e2-4d89-a69e-08cb3c9f34d2 # bogus
provider: some-keycloak
provider:
some-keycloak:
token-uri: https://some.bogus/token/uri
Run Code Online (Sandbox Code Playgroud)
您还可以使用已经使用的相同内容mockserver
来模拟 REST 资源,也可以模拟授权服务器并响应令牌请求。为此,您可以将或任何您用来分别为测试提供属性的内容配置mockserver
为。token-uri
src/test/resources/application.yml
WebClient
直接注入WebClient
在 bean 中提供 a 的推荐方法是通过注入WebClient.Builder
,它由 Spring Boot预先配置。这也保证了WebClient
测试中的配置与生产中的配置完全相同。您可以声明WebClientCustomizer
bean 来进一步配置此构建器。这就是它在上面提到的我的展示存储库中实现的方式。
@Primary
在@Bean
a@Configuration
或@TestConfiguration
我也尝试过,发现它并不总是按照人们期望的方式工作,可能是因为 Spring 加载和实例化 bean 定义的顺序。例如,只有当它是测试类中的类ReactiveOAuth2AuthorizedClientManager
时才使用模拟,但如果它是ed 则不使用模拟。在接口上使用并使用测试类实现它也是行不通的。因此,为了避免将该类放在我需要的每个集成测试中,我宁愿选择此处介绍的方法。@TestConfiguration
static nested
@Import
static nested @TestConfiguration
static nested
@TestComponent
我只针对Client Credentials
资助类型测试了我的方法,但我认为它也可以针对其他资助类型进行调整或扩展。
归档时间: |
|
查看次数: |
3622 次 |
最近记录: |