为何Spring Security配置permitAll()后/upload端点仍需认证并返回403?
问题:Spring Boot 3.2.2 + Spring Security中/upload端点permitAll()不生效
我使用Spring Boot 3.2.2搭配Spring Security,已为3个端点配置permitAll(),但仅/upload端点不生效:它仍要求认证,且认证后返回403。即使将其移至ADMIN或USER的requestMatchers()中,依然返回403,其他端点则正常工作。
这些端点的唯一区别是:/upload是接收MultipartFile的POST请求,其余为简单GET请求。
控制器代码示例
@PostMapping("/upload") public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) { // 业务逻辑代码 }
安全配置代码示例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.formLogin(Customizer.withDefaults()) .authorizeHttpRequests((authorize) -> authorize .requestMatchers("/test/user").hasRole(UserRole.USER.name()) .requestMatchers("/test/admin").hasRole(UserRole.ADMIN.name()) .requestMatchers("/test/no-role", "/upload", "/api/ai/generate").permitAll() .anyRequest().authenticated() ); return http.build(); }
更新:DEBUG级别日志
2024-02-13T12:50:34.003+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing POST /upload 2024-02-13T12:50:34.005+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:8080/upload 2024-02-13T12:50:34.007+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code 2024-02-13T12:50:34.009+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing POST /error 2024-02-13T12:50:34.009+02:00 DEBUG 16611 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=7579E5587784A8B4847F2571BCD188A2], Granted Authorities=[ROLE_ADMIN]]] 2024-02-13T12:50:34.009+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Secured POST /error 2024-02-13T12:50:34.009+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for POST "/error", parameters={} 2024-02-13T12:50:34.010+02:00 DEBUG 16611 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest) 2024-02-13T12:50:34.010+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor 2024-02-13T12:50:34.012+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json] 2024-02-13T12:50:34.013+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Tue Feb 13 12:50:34 EET 2024, status=403, error=Forbidden, path=/upload}] 2024-02-13T12:50:34.022+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor 2024-02-13T12:50:34.022+02:00 DEBUG 16611 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 403
问题原因与解决方案
从DEBUG日志可以直接定位问题:Invalid CSRF token found for http://localhost:8080/upload,这说明CSRF保护拦截了你的POST请求。
Spring Security默认会对所有非GET请求启用CSRF校验,哪怕端点配置了permitAll(),CSRF校验依然会生效——这就是为什么其他GET端点正常,而POST的/upload出问题的核心原因。
解决方案一:给/upload端点禁用CSRF校验
修改SecurityFilterChain配置,添加csrf忽略规则:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.formLogin(Customizer.withDefaults()) .csrf(csrf -> csrf.ignoringRequestMatchers("/upload")) // 忽略该路径的CSRF校验 .authorizeHttpRequests((authorize) -> authorize .requestMatchers("/test/user").hasRole(UserRole.USER.name()) .requestMatchers("/test/admin").hasRole(UserRole.ADMIN.name()) .requestMatchers("/test/no-role", "/upload", "/api/ai/generate").permitAll() .anyRequest().authenticated() ); return http.build(); }
解决方案二:在请求中携带有效CSRF Token
如果不想禁用CSRF保护,可在POST请求中携带CSRF Token:
- 前端:从Cookie中获取
XSRF-TOKEN,在请求头中添加X-XSRF-TOKEN字段,值为获取到的Token - 后端测试:用Postman等工具先登录获取Cookie中的CSRF Token,再在请求头中添加对应字段
注意事项
- 若
/upload是公开接口(无需用户认证),推荐方案一,这类接口通常不需要CSRF保护 - 若接口需要用户认证,建议保留CSRF保护,采用方案二
内容的提问来源于stack exchange,提问作者Dinu Nicolae
相关产品推荐
相关产品推荐

