Spring Security配置中permitAll端点仍返回403的求助
Spring Security 6.2.1中
permitAll()端点返回403错误排查与解决 我使用Spring Boot 3.2.2 + Spring Security 6.2.1搭建供React调用的Web API项目,大部分配置已完成,但设置了permitAll()的登录端点始终返回403错误。
当前SecurityFilterChain配置
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { System.out.println(httpSecurity); return httpSecurity .authorizeHttpRequests((auth) -> auth .requestMatchers("/api/myproject/admin/**") // 尝试过移除该条件 .hasRole("ADMIN") .requestMatchers(HttpMethod.POST, "/public/login") // 尝试过不带上下文路径 .permitAll() .requestMatchers(HttpMethod.POST, "/api/myproject/public/login") // 尝试过带上下文路径 .permitAll() .anyRequest().authenticated() ) .csrf(AbstractHttpConfigurer::disable) // 尝试过禁用CSRF .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .build(); }
已尝试的无效方案
- 为登录端点同时配置GET和POST请求(理想情况是用POST)
- 禁用CSRF防护
- 尝试带/不带上下文路径(
/api/myproject为项目上下文路径)的端点匹配规则 - 明确指定HTTP POST请求方法
- 移除所有规则,仅保留登录路由的
permitAll()配置
仅当设置anyRequest().permitAll()时,所有端点(包括登录)才能正常访问。
补充代码信息
登录控制器代码
@Slf4j @RestController @RequestMapping("/public") public class LoginController { @Autowired private LoginService loginService; @PostMapping("/login") public ResponseEntity<LoginResponse> generateUserToken(@RequestBody LoginRequest loginRequest) { log.info("generateUserToken Started"); String token = loginService.login(loginRequest.getEmailId(), loginRequest.getPwd()); return ResponseEntity.ok(new LoginResponse(token)); } }
调用API的curl命令
curl --location 'http://localhost:8080/api/myproject/public/login' \ --header 'Content-Type: application/json' \ --header 'Cookie: JSESSIONID=9E2DD3E4A04619324786B0F595BD96FD' \ --data-raw '{ "emailId": "dkjkaj@gmail.com", "pwd": "jhdsjhdj" }'
问题原因及解决方法
1. 上下文路径匹配冗余
Spring Security的requestMatchers匹配的是上下文路径之后的请求路径,不需要额外拼接项目上下文路径/api/myproject。你的控制器映射是/public/login,所以只需配置HttpMethod.POST, "/public/login"即可,多余的带上下文路径的匹配规则会干扰正常匹配逻辑。
2. JWT过滤器拦截了未授权请求
你添加的jwtAuthFilter在UsernamePasswordAuthenticationFilter之前执行,如果该过滤器对所有请求(包括登录端点)都进行JWT校验,那么即使设置了permitAll(),未携带token的登录请求也会被过滤器拦截返回403。
解决方式:
在JWT过滤器中排除登录端点,直接放行:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String path = request.getRequestURI(); // 排除登录端点,跳过JWT校验 if (path.equals("/public/login")) { filterChain.doFilter(request, response); return; } // 其他JWT校验逻辑... }
或者在Security配置中,为登录端点禁用JWT过滤器:
.authorizeHttpRequests((auth) -> auth .requestMatchers(HttpMethod.POST, "/public/login").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) // 给登录端点禁用JWT过滤器 .requestMatchers(HttpMethod.POST, "/public/login").filters(f -> f.disable())
3. 添加无状态会话配置
你的请求携带了JSESSIONID,这是Spring Security默认创建会话的产物,但JWT认证应该是无状态的。在Security配置中添加无状态会话配置,避免会话干扰:
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
4. 简化后的完整Security配置
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.POST, "/public/login").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .build(); }
内容的提问来源于stack exchange,提问作者Saurabh Tiwari
相关产品推荐
相关产品推荐

