Spring Boot 3.1.0中Spring Security禁止POST请求的问题排查
Spring Boot 3.1.0集成Spring Security时POST请求403问题
环境配置
插件配置
plugins { id 'java' id 'org.springframework.boot' version '3.1.0' id 'io.spring.dependency-management' version '1.1.0' }
依赖配置
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security'
测试端点代码
@Slf4j @RestController @RequestMapping("/open") public class OpenedController { @GetMapping public String secureHi() { log.info("Hi, I am opened."); return "Hi, I am opened."; } @PostMapping public String secureHPost(@RequestBody MyUser myUser) { log.info("Hi, I am opened POST. myUser ={}", myUser); return "Hi, I am opened POST."; } }
安全配置类
@Configuration @EnableWebSecurity public class SecurityConfiguration { @Bean public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { UserDetails admin = User.withUsername("chitta") .password(passwordEncoder.encode("chitta")) .roles("ADMIN") .build(); UserDetails user = User.withUsername("aaa") .password(passwordEncoder.encode("aa")) .roles("USER") .build(); return new InMemoryUserDetailsManager(admin, user); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( (authorize) -> authorize .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll() .requestMatchers("/open**").permitAll() .anyRequest().denyAll() ); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
问题现象
访问GET /open正常,但访问POST /open返回403 Forbidden:
{ "timestamp": "2023-06-16T08:50:43.240+00:00", "status": 403, "error": "Forbidden", "path": "/open" }
尝试过.requestMatchers(HttpMethod.POST, "/open**").permitAll(),问题依旧。
解决方案
问题根源是Spring Security默认开启了CSRF防护,对于POST、PUT等非安全HTTP方法,会强制验证CSRF令牌,而你的请求未携带该令牌,因此被拦截返回403。
有两种处理方案:
关闭CSRF保护(适合无状态REST API场景):
在securityFilterChain配置中添加关闭CSRF的代码,修改后的配置如下:@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) // 关闭CSRF防护 .authorizeHttpRequests( (authorize) -> authorize .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll() .requestMatchers("/open**").permitAll() .anyRequest().denyAll() ); return http.build(); }携带CSRF令牌(适合有状态Web应用场景):
- 浏览器端:可通过访问
/_csrf端点获取令牌,在请求头中添加X-CSRF-TOKEN字段携带令牌; - API客户端:在请求头中直接添加
X-CSRF-TOKEN字段,值为预先获取的令牌。
- 浏览器端:可通过访问
内容的提问来源于stack exchange,提问作者CR Sardar
相关产品推荐
相关产品推荐

