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

Spring Boot测试返回401但应用返回403的问题排查与解决

问题原因分析

实际应用和MockMvc测试行为不一致的核心原因是错误页面的处理流程差异:

  • 生产环境中,登录接口返回401后,Spring Boot会自动转发到/error端点;而你的Security配置中/error端点不允许匿名访问,所以触发Http403ForbiddenEntryPoint,最终返回403。
  • MockMvc默认不会自动处理这种错误转发逻辑,登录失败后直接返回原始的401响应,不会走到/error的拦截环节。
解决方案

方法1:让MockMvc模拟错误页面转发流程

修改MockMvc的配置,添加错误处理的模拟,让它和生产环境一致:

@SpringBootTest
@AutoConfigureMockMvc
class LoginTest {

    @Autowired
    lateinit var mockMvc: MockMvc

    @Test
    fun `test login returns 403`() {
        mockMvc.perform(MockMvcRequestBuilders.post("/login"))
            .andExpect(status().isForbidden)
            .andDo(MockMvcResultHandlers.print())
    }
}

如果还是不行,手动配置MockMvc启用错误调度:

@Autowired
lateinit var webApplicationContext: WebApplicationContext

lateinit var mockMvc: MockMvc

@BeforeEach
fun setup() {
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
        .apply(springSecurity())
        .setDispatcherServletCustomizer { servlet ->
            servlet.setThrowExceptionIfNoHandlerFound(true)
            servlet.setDispatchOptionsRequest(true)
            servlet.setDispatchTraceRequest(true)
        }
        .build()
}

方法2:调整Security配置,直接在登录接口返回403

如果不想依赖/error的转发逻辑,可以修改SecurityConfig,让登录失败后直接返回403,而不是先返回401再转发:

@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .authorizeHttpRequests { auth ->
                auth.anyRequest().authenticated()
            }
            .formLogin { form ->
                form.loginProcessingUrl("/login")
                    .failureHandler { request, response, exception ->
                        response.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.reasonPhrase)
                    }
            }
            .exceptionHandling { exceptions ->
                exceptions.authenticationEntryPoint(Http403ForbiddenEntryPoint())
            }
        return http.build()
    }
}

这样不管是生产环境还是MockMvc测试,都会直接返回403,行为一致。

方法3:使用TestRestTemplate模拟真实请求

如果MockMvc的模拟还是有差异,可以用TestRestTemplate来测试,它会完全模拟HTTP请求流程,包括错误页面转发:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class LoginTest {

    @Autowired
    lateinit var restTemplate: TestRestTemplate

    @Test
    fun `test login returns 403`() {
        val response = restTemplate.postForEntity("/login", null, String::class.java)
        assertThat(response.statusCode).isEqualTo(HttpStatus.FORBIDDEN)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 11:12:47