如何阻止@SpringBootTest自动发现OAuth2配置?
问题解决:Spring Boot集成测试中Okta OIDC元数据请求超时
核心问题
你的应用存在两条Spring Security过滤器链:
- 链1(优先级1):针对SSO登录端点,通过okta-spring-boot作为OAuth2资源服务器验证外部授权服务器的JWT,启动时会自动请求外部OIDC元数据(
.well-known/openid-configuration) - 链2(优先级2):针对业务端点,使用自定义
JwtTokenUtil验证自研JWT
在测试业务端点时,框架仍会触发链1的元数据请求,导致测试超时或依赖外部服务。
解决方案
方案1:Mock JwtDecoder(最简方案)
直接Mock Okta自动配置的JwtDecoder Bean,阻止其发起外部请求。测试的业务端点不会用到这个Bean,完全不影响测试逻辑。
修改后的测试代码
package com.mobileapi.event_log; import com.fasterxml.jackson.databind.ObjectMapper; import com.mobileapi.config.JwtTokenUtil; import com.mobileapi.results_services.analytics_log_event.dto.AnalyticsLogEventDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import static com.mobileapi.filter.CustomAuthenticationProcessingFilter.JWT_TOKEN_PREFIX; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @TestPropertySource("classpath:application-test.properties") class EventLogControllerIntegrationTests { private static final String EVENT_LOG_REQUEST_PATH = "/api/v1/event-log"; private static final String DEVICE_ID = "abc"; @MockBean JwtTokenUtil jwtTokenUtil; @MockBean EventLogService eventLogService; @MockBean // 新增:Mock Okta的JwtDecoder,阻止外部请求 JwtDecoder jwtDecoder; @Autowired private MockMvc mockMvc; private ObjectMapper objectMapper; @BeforeEach public void setup() { objectMapper = new ObjectMapper(); } @Test @DisplayName("succeeds with a valid bearer token") void succeedsWithValidBearerToken() throws Exception { final String token = "abc123"; when(jwtTokenUtil.validateToken(token)).thenReturn(true); mockMvc.perform( post(EVENT_LOG_REQUEST_PATH) .queryParam("deviceId", DEVICE_ID) .content(objectMapper.writeValueAsString(mock(AnalyticsLogEventDTO.class))) .contentType(MediaType.APPLICATION_JSON) .header(HttpHeaders.AUTHORIZATION, JWT_TOKEN_PREFIX + token) .with(anonymous()) ) .andDo(print()) .andExpect(status().isAccepted()); } @Nested @DisplayName("returns unauthorized") class ReturnsUnauthorized { @Test @DisplayName("when there is no bearer token") void withoutBearerToken() throws Exception { mockMvc.perform(post(EVENT_LOG_REQUEST_PATH) .queryParam("deviceId", DEVICE_ID) .content(objectMapper.writeValueAsString(mock(AnalyticsLogEventDTO.class))) .contentType(MediaType.APPLICATION_JSON) .with(anonymous())) .andDo(print()) .andExpect(status().isUnauthorized()); } @Test @DisplayName("when the bearer token is invalid") void whenTokenIsInvalid() throws Exception { final String token = "abc123"; when(jwtTokenUtil.validateToken(token)).thenReturn(false); mockMvc.perform( post(EVENT_LOG_REQUEST_PATH) .queryParam("deviceId", DEVICE_ID) .content(objectMapper.writeValueAsString(mock(AnalyticsLogEventDTO.class))) .contentType(MediaType.APPLICATION_JSON) .header(HttpHeaders.AUTHORIZATION, JWT_TOKEN_PREFIX + token) .with(anonymous()) ) .andDo(print()) .andExpect(status().isUnauthorized()); } } }
方案2:指定本地JWK元数据(适用于需测试OAuth2链的场景)
如果后续需要测试SSO登录端点,可在测试属性中指定本地JWK文件,避免外部请求:
- 在
src/test/resources下创建mock-jwks.json,填入结构合法的JWK(无需真实密钥):
{ "keys": [ { "kty": "RSA", "e": "AQAB", "n": "tYxQZ8G3GzXa9Y7F4D2S1A6Z9X8C7V4B3N1M0K9L8J7H6G5F4D3S2A1Q0W9E8R7T6Y5U4I3O2P1", "kid": "test-key" } ] }
- 修改
application-test.properties:
okta.oauth2.audience=https://test.com okta.oauth2.issuer=https://test.com/ spring.security.oauth2.resourceserver.jwt.jwk-set-uri=classpath:mock-jwks.json
方案3:排除Okta自动配置(彻底禁用OAuth2链)
如果测试完全不需要OAuth2链,可直接排除Okta的自动配置类:
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK, exclude = OktaOAuth2AutoConfiguration.class )
方案说明
- 方案1是当前场景的最优解:无需额外配置,Mock后直接阻断外部请求,不影响业务端点测试
- 方案2适合需要验证OAuth2链的测试场景,保持配置完整性的同时避免外部依赖
- 方案3适合完全不需要OAuth2功能的测试,彻底移除Okta相关配置
内容的提问来源于stack exchange,提问作者Danny Allen
相关产品推荐
相关产品推荐

