Spring Boot配置requestMatchers.permitAll()仍返回403错误排查求助
我太懂你这种抓耳挠腮的感觉了——明明在SecurityConfig里给/api/user/test配置了permitAll,结果还是硬返回403,只能靠关掉整个Spring Security或者把所有请求设为permitAll才能绕过去,这搁谁都得崩溃。我对着你的代码和Debug日志捋了一圈,帮你梳理几个大概率的问题点,你可以挨个排查:
1. 跨域预检请求(OPTIONS)被拦截了
这绝对是踩坑率最高的情况!如果你的请求是从前端页面发过来的,浏览器会先偷偷发一个OPTIONS方法的预检请求,而你现在的SecurityConfig里只给POST方法的/api/user/test开了permitAll,OPTIONS请求根本匹配不上这个规则,直接撞到anyRequest().authenticated()上,可不就返回403了嘛!
解决方法很简单,要么给这个路径开放所有HTTP方法,要么单独给OPTIONS请求开绿灯:
// 方法一:允许/api/user/test的所有HTTP方法(包括预检请求) .requestMatchers("/api/user/test").permitAll() // 方法二:单独放行OPTIONS,同时保留POST的permitAll .requestMatchers(HttpMethod.OPTIONS, "/api/user/test").permitAll() .requestMatchers(HttpMethod.POST, "/api/user/test").permitAll()
另外建议顺便把跨域配置加上,避免后续再踩同类坑:
http.cors(cors -> cors.configurationSource(corsConfigurationSource())) // 自定义跨域规则(示例,替换成你的前端域名) private CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }
2. 路径匹配不精确
你得仔细核对实际发起的请求路径和配置的路径是不是完全一致:
- 有没有多/少了末尾斜杠?比如你请求的是
/api/user/test/(带尾巴斜杠),但配置的是/api/user/test,这会导致匹配失败; - 有没有带额外的路径参数?比如请求的是
/api/user/test/123,但你配置的是精确匹配/api/user/test,自然也匹配不上。
如果是需要前缀匹配的场景,你可以用/api/user/test/**来覆盖子路径,但你的情况是精确匹配,所以务必保证请求路径和配置的完全一致。
3. 自定义JWT过滤器的隐性干扰
看你的JWTAuthenticationFilter代码,虽然定义了PERMIT_ALL_PREFIXES列表,但实际逻辑里并没有用它来判断是否跳过token校验?不过当前的逻辑是对的:只要有Bearer token就校验,没有或无效就直接放行filterChain。
不过你可以检查下:有没有可能之前测试时没注释掉response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)那段代码,导致response被提前设为401,后续又被Spring Security转为403?虽然你现在注释掉了,但还是可以确认下过滤器里有没有其他修改response的逻辑。
4. 匿名认证被无意禁用
Spring Security默认是开启匿名认证的,但如果你的配置里不小心加了http.anonymous(AnonymousConfigurer::disable),那哪怕是permitAll路径,匿名请求也会被直接拒绝返回403。看你的SecurityConfig里没有这个配置,这个可能性比较低,但可以顺手确认下。
最后给你一个调整后的参考配置
我把跨域、OPTIONS处理、认证入口点都加上了,你可以试试:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(CsrfConfigurer::disable) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/user/test").permitAll() .requestMatchers("/error").permitAll() .anyRequest().authenticated() ) // 显式开启匿名认证,确保permitAll路径能正常接受匿名请求 .anonymous(anonymous -> anonymous.authorities("ROLE_ANONYMOUS")) // 配置认证入口点,非permitAll请求返回401而非403,更符合语义 .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: 无效或缺失JWT令牌"); }) ) .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } private CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); // 替换为你的前端域名 config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } }
如果排查完这些还是不行,你可以把请求的具体信息(比如实际请求方法、完整路径、请求头)贴出来,再进一步定位问题。
内容来源于stack exchange




