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

如何阻止@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文件,避免外部请求:

  1. 在src/test/resources下创建mock-jwks.json,填入结构合法的JWK(无需真实密钥):
{
  "keys": [
    {
      "kty": "RSA",
      "e": "AQAB",
      "n": "tYxQZ8G3GzXa9Y7F4D2S1A6Z9X8C7V4B3N1M0K9L8J7H6G5F4D3S2A1Q0W9E8R7T6Y5U4I3O2P1",
      "kid": "test-key"
    }
  ]
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:34:49