Чтобы уточнить ответ от Eleftheria Stein-Kousathana, я изменил это, чтобы сделать возможным:
1) Создайте класс JwtDecoderFactoryBean
, чтобы иметь возможность провести модульное тестирование JwtDecoder
инастроенные валидаторы:
@Component
public class JwtDecoderFactoryBean implements FactoryBean<JwtDecoder> {
private final OAuth2ResourceServerProperties properties;
private final SecuritySettings securitySettings;
private final Clock clock;
public JwtDecoderFactoryBean(OAuth2ResourceServerProperties properties,
SecuritySettings securitySettings,
Clock clock) {
this.properties = properties;
this.securitySettings = securitySettings;
this.clock = clock;
}
@Override
public JwtDecoder getObject() {
JwtTimestampValidator timestampValidator = new JwtTimestampValidator();
timestampValidator.setClock(clock);
JwtIssuerValidator issuerValidator = new JwtIssuerValidator(securitySettings.getJwtIssuer());
JwtAudienceValidator audienceValidator = new JwtAudienceValidator(securitySettings.getJwtApplicationId());
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(
timestampValidator,
issuerValidator,
audienceValidator);
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(properties.getJwt().getJwkSetUri())
.build();
decoder.setJwtValidator(validator);
return decoder;
}
@Override
public Class<?> getObjectType() {
return JwtDecoder.class;
}
}
Я также извлек AudienceValidator
из исходного кода во внешний класс и переименовал его в JwtAudienceValidator
.
2) Удалите JwtDecoder
@Bean
метод из конфигурации безопасности, поэтому он выглядит следующим образом:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}
}
3) Создайте компонент Clock
в некотором @Configuration
классе:
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
(Это необходимо длямодульное тестирование истечения времени действия токенов)
С помощью этой настройки теперь можно написать модульный тест для настройки JwtDecoder
, которая является фактической настройкой, используемой приложением:
// actual @Test methods ommitted, but they can use this private method
// to setup a JwtDecoder and test some valid/invalid JWT tokens.
@NotNull
private JwtDecoder createDecoder(String currentTime, String issuer, String audience) {
OAuth2ResourceServerProperties properties = new OAuth2ResourceServerProperties();
properties.getJwt().setJwkSetUri(
"https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_ropc_flow");
JwtDecoderFactoryBean factoryBean = new JwtDecoderFactoryBean(properties,
new SecuritySettings(audience, issuer),
Clock.fixed(Instant.parse(currentTime),
ZoneId.systemDefault()));
//noinspection ConstantConditions - getObject never returns null in this case
return factoryBean.getObject();
}
Наконец, @WebMvcTest
должен иметь макет JwtDecoder
, поскольку реальный не запускается больше с тестовым срезом @WebMvcTest
(из-за использования фабричного компонента). Это хороший IMO, так как в противном случае мне нужно было определить свойства для реального JwtDecoder
, который так или иначе не использовался. Как следствие, мне больше не нужен профиль controller-test
в тесте.
Поэтому просто объявите поле наподобие этого:
@MockBean
private JwtDecoder jwtDecoder;
или создайте вложенный класс конфигурации теста:
@TestConfiguration
static class TestConfig {
@Bean
public JwtDecoder jwtDecoder() {
return mock(JwtDecoder.class);
}
}