gpt4 book ai didi

java - 使用 Spring Security 在 @WebMvcTest 中测试 JwtDecoder

转载 作者:行者123 更新时间:2023-12-03 23:14:19 25 4
gpt4 key购买 nike

我正在使用带有 spring-security-oauth2-resource-server:5.2.0.RELEASE 的 Spring Boot 2.2.1 .我想写一个集成测试来测试安全性是否可以。

我有这个 WebSecurityConfigurerAdapter在我的应用程序中定义:

import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

private final OAuth2ResourceServerProperties properties;
private final SecuritySettings securitySettings;

public WebSecurityConfiguration(OAuth2ResourceServerProperties properties, SecuritySettings securitySettings) {
this.properties = properties;
this.securitySettings = securitySettings;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}

@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder result = NimbusJwtDecoder.withJwkSetUri(properties.getJwt().getJwkSetUri())
.build();

OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefault(),
new AudienceValidator(securitySettings.getApplicationId()));

result.setJwtValidator(validator);
return result;
}

private static class AudienceValidator implements OAuth2TokenValidator<Jwt> {

private final String applicationId;

public AudienceValidator(String applicationId) {
this.applicationId = applicationId;
}

@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
if (token.getAudience().contains(applicationId)) {
return OAuth2TokenValidatorResult.success();
} else {
return OAuth2TokenValidatorResult.failure(
new OAuth2Error("invalid_token", "The audience is not as expected, got " + token.getAudience(),
null));
}
}
}
}

它有一个自定义 validator 来检查 token 中的受众 ( aud ) 声明。

我目前有这个测试,它有效,但它根本不检查观众声明:
@WebMvcTest(UserController.class)
@EnableConfigurationProperties({SecuritySettings.class, OAuth2ResourceServerProperties.class})
@ActiveProfiles("controller-test")
class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@Test
void testOwnUserDetails() throws Exception {
mockMvc.perform(get("/api/users/me")
.with(jwt(createJwtToken())))
.andExpect(status().isOk())
.andExpect(jsonPath("userId").value("AZURE-ID-OF-USER"))
.andExpect(jsonPath("name").value("John Doe"));
}

@Test
void testOwnUserDetailsWhenNotLoggedOn() throws Exception {
mockMvc.perform(get("/api/users/me"))
.andExpect(status().isUnauthorized());
}

@NotNull
private Jwt createJwtToken() {
String userId = "AZURE-ID-OF-USER";
String userName = "John Doe";
String applicationId = "AZURE-APP-ID";

return Jwt.withTokenValue("fake-token")
.header("typ", "JWT")
.header("alg", "none")
.claim("iss",
"https://b2ctestorg.b2clogin.com/80880907-bc3a-469a-82d1-b88ffad655df/v2.0/")
.claim("idp", "LocalAccount")
.claim("oid", userId)
.claim("scope", "user_impersonation")
.claim("name", userName)
.claim("azp", applicationId)
.claim("ver", "1.0")
.subject(userId)
.audience(Set.of(applicationId))
.build();
}
}

我还有一个 controller-test 的属性文件包含应用程序 ID 和 jwt-set-uri 的配置文件:
security-settings.application-id=FAKE_ID
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://b2ctestorg.b2clogin.com/b2ctestorg.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_ropc_flow

也许 JwtDecoder 没有使用,因为 Jwt 是手动创建的?我如何确保在测试中调用了 JwtDecoder?

最佳答案

为了详细说明 Eleftheria Stein-Kousathana 的答案,我进行了更改以使其成为可能:

1) 创建一个 JwtDecoderFactoryBean类能够对 JwtDecoder 进行单元测试和配置的 validator :

@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一些 bean @Configuration类(class):
    @Bean
public Clock clock() {
return Clock.systemDefaultZone();
}

(这是 token 时间到期的单元测试所必需的)

通过此设置,现在可以为 JwtDecoder 编写单元测试。 setup 是应用程序使用的实际设置:

// 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 开始的测试切片(由于使用了工厂 bean)。这是很好的 IMO,否则,我需要为真实的 JwtDecoder 定义属性无论如何都没有被使用。因此,我不需要 controller-test在测试中不再配置文件。

所以只需声明一个这样的字段:
@MockBean
private JwtDecoder jwtDecoder;

或者创建一个嵌套的测试配置类:
 @TestConfiguration
static class TestConfig {
@Bean
public JwtDecoder jwtDecoder() {
return mock(JwtDecoder.class);
}
}

关于java - 使用 Spring Security 在 @WebMvcTest 中测试 JwtDecoder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58762870/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com