You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用TestRestTemplate禁用Spring Security测试主应用?是否需测试该类?

解决方案

一、在集成测试中禁用Spring Security(避免依赖Auth0服务)

提供三种可行方案,按需选择:

方法1:用@MockBean替代真实SecurityConfig

直接在测试类上添加@MockBean(SecurityConfig.class),Spring Boot会自动用Mock实例替换真实的安全配置类,跳过OAuth2相关逻辑加载,测试可正常访问actuator端点:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = HealthDataImporterApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
@MockBean(SecurityConfig.class) // 添加该行
public class HealthDataImporterApplicationTest {

    @Autowired
    private TestRestTemplate testRestTemplate;
    @LocalServerPort
    private int serverPort;

    @Test
    public void health() {
        assertThat(read(get("health"), "$.status")).isEqualTo("UP");
    }

    private String get(final String resource) {
        return testRestTemplate.getForObject(String.format("http://localhost:%s/actuator/%s", serverPort, resource),
                String.class);
    }
}

方法2:编写测试专用安全配置

创建一个完全开放权限的测试用安全配置,覆盖真实配置:

// 测试专用安全配置类
@EnableWebSecurity
public class TestSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .anyRequest().permitAll(); // 允许所有请求访问
        return http.build();
    }
}

修改测试类,指定加载该测试配置:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {HealthDataImporterApplication.class, TestSecurityConfig.class}, 
               webEnvironment = WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
public class HealthDataImporterApplicationTest {
    // 原有测试代码保持不变
}

方法3:通过配置属性禁用OAuth2资源服务器

在application-test.properties中添加以下配置,让Spring Security使用无操作JWT解码器,避免调用Auth0服务(需Spring Security 5.6+版本支持):

spring.security.oauth2.resourceserver.jwt.decoder=noop

二、是否有必要测试SecurityConfig?

非常有必要,但建议将安全配置测试与应用集成测试分离,采用单元测试或专项测试验证:

1. 测试SecurityFilterChain规则

验证安全规则是否符合预期,比如actuator端点权限、其他接口权限要求等:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SecurityConfig.class)
public class SecurityConfigTest {

    @Autowired
    private SecurityFilterChain securityFilterChain;
    @Autowired
    private WebApplicationContext context;

    @Test
    public void testActuatorGetRequestsPermitted() throws Exception {
        MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(securityFilterChain))
                .build();

        // 验证actuator/health允许匿名访问
        mockMvc.perform(get("/actuator/health"))
                .andExpect(status().isOk());
    }

    @Test
    public void testOtherRequestsRequireScope() throws Exception {
        MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(securityFilterChain))
                .build();

        // 验证非actuator请求需权限
        mockMvc.perform(get("/api/data"))
                .andExpect(status().isUnauthorized());

        // 验证携带正确scope的请求可正常访问
        mockMvc.perform(get("/api/data")
                        .with(jwt().jwt(jwt -> jwt.claim("scope", "scope:scope"))))
                .andExpect(status().isOk());
    }
}

2. 测试JwtDecoder验证逻辑

单独验证JWT的受众(aud)、签发者(iss)等声明的校验逻辑:

public class JwtDecoderTest {

    private JwtDecoder jwtDecoder;
    private final String testIssuer = "https://test-issuer.com";
    private final String testAudience = "test-audience";

    @BeforeEach
    void setUp() {
        // 提取原SecurityConfig中JwtDecoder的构建逻辑,便于测试
        NimbusJwtDecoder decoder = JwtDecoders.fromOidcIssuerLocation(testIssuer);
        decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
                JwtValidators.createDefaultWithIssuer(testIssuer),
                new AudienceValidator(testAudience)
        ));
        this.jwtDecoder = decoder;
    }

    @Test
    void testValidJwtWithCorrectAudience() {
        // 生成包含正确aud和iss的测试JWT(可使用JWT生成库或硬编码测试用token)
        String validJwt = generateTestJwt(testIssuer, testAudience);
        Jwt jwt = jwtDecoder.decode(validJwt);
        assertThat(jwt.getAudience()).contains(testAudience);
    }

    @Test
    void testInvalidJwtWithWrongAudience() {
        String invalidJwt = generateTestJwt(testIssuer, "wrong-audience");
        assertThatThrownBy(() -> jwtDecoder.decode(invalidJwt))
                .isInstanceOf(OAuth2TokenValidationException.class);
    }

    // 辅助方法:生成测试用JWT
    private String generateTestJwt(String issuer, String audience) {
        // 实现JWT生成逻辑,例如使用JJWT库
        return Jwts.builder()
                .setIssuer(issuer)
                .setAudience(Collections.singletonList(audience))
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "test-secret")
                .compact();
    }
}

内容的提问来源于stack exchange,提问作者Jonathan Hagen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 22:00:58