Spring Boot3中SecurityFilterChain API请求匹配器配置异常排查
解决方案
问题出在Spring Security对API路径的异常处理逻辑上——你给/api/**开了permitAll,但当API密钥校验失败抛出异常时,Spring Security还是用了默认的网页端认证入口(跳登录页),而不是返回JSON错误。要解决这个,得给API路径单独配置异常处理规则,同时确保过滤器位置正确。
1. 编写返回JSON格式的认证异常处理器
先实现一个自定义的AuthenticationEntryPoint,专门处理API请求的未授权场景:
@Component public class ApiAuthEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 用Jackson将错误信息序列化为JSON输出 new ObjectMapper().writeValue(response.getOutputStream(), Map.of("code", 401, "message", "API密钥缺失或无效") ); } }
2. 配置SecurityFilterChain时区分API与网页路径
有两种配置方式,任选其一即可:
方式一:拆分两个独立的安全过滤器链
分别对API路径和普通网页路径做针对性配置:
@Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http, ApiAuthEntryPoint apiEntryPoint) throws Exception { http .securityMatcher("/api/**") // 仅匹配API路径 .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) .exceptionHandling(ex -> ex.authenticationEntryPoint(apiEntryPoint)) // 指定API专属异常处理器 .addFilterBefore(你的API密钥过滤器实例, UsernamePasswordAuthenticationFilter.class) // 将API密钥过滤器放在Spring Security认证过滤器之前 .csrf(csrf -> csrf.disable()); // API场景通常不需要CSRF保护 return http.build(); } @Bean public SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/**") // 匹配所有非API的网页路径 .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .formLogin(form -> form.loginPage("/login").permitAll()); return http.build(); }
方式二:在同一个过滤器链中绑定路径专属异常处理器
如果不想拆分链,也可以在同一个配置里给API路径单独指定异常处理逻辑:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, ApiAuthEntryPoint apiEntryPoint) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/api/**").permitAll() .anyRequest().authenticated() ) .exceptionHandling(ex -> ex // 为/api/**路径绑定专属的异常处理器 .defaultAuthenticationEntryPointFor(apiEntryPoint, new AntPathRequestMatcher("/api/**")) ) .addFilterBefore(你的API密钥过滤器实例, UsernamePasswordAuthenticationFilter.class) .csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")) // 忽略API路径的CSRF检查 .formLogin(form -> form.loginPage("/login").permitAll()); return http.build(); }
核心原理
当你仅给/api/**设置permitAll时,Spring Security的默认异常处理器依然会接管认证失败的请求(跳转登录页)。通过给API路径绑定专属的AuthenticationEntryPoint,可以让这类请求返回JSON格式的401错误,同时保证网页请求依然遵循正常的登录跳转逻辑。
内容的提问来源于stack exchange,提问作者Chuck M
相关产品推荐
相关产品推荐

