Spring Security配置CSRF后非GET请求触发403错误的解决咨询
问题原因分析
Spring Security默认开启CSRF(跨站请求伪造)防护机制,核心目的是拦截恶意站点通过浏览器自动携带用户Cookie发起的未授权修改请求。
对于POST/PUT/DELETE这类会修改服务器数据的非幂等请求,Spring Security会强制校验请求中是否包含有效的CSRF Token:
- 你的请求中没有携带该Token,即使已经通过ADMIN身份认证,也会被CSRF过滤器判定为非法请求,返回
403 Forbidden。 - 而
GET请求属于幂等请求,不会触发CSRF校验,所以ADMIN和USER角色都能正常访问。
安全解决方案
不要全局禁用CSRF(会带来安全风险),可以根据你的API使用场景选择以下安全方案:
1. 针对浏览器端请求:携带CSRF Token
如果你的API是供前端浏览器调用:
- 表单提交场景:在表单中添加Spring Security自动生成的CSRF Token隐藏域:
<form method="post" action="/topics"> <input type="hidden" name="_csrf" value="${_csrf.token}"/> <!-- 其他表单字段 --> </form> - AJAX/REST请求场景:
- 前端先从Cookie中读取名为
XSRF-TOKEN的CSRF Token(Spring Security默认会将Token写入该Cookie); - 在非GET请求的请求头中添加
X-XSRF-TOKEN字段,值为读取到的Token。示例(JavaScript):const token = document.cookie.split('; ') .find(row => row.startsWith('XSRF-TOKEN=')) ?.split('=')[1]; fetch('/topics', { method: 'POST', headers: { 'X-XSRF-TOKEN': token, 'Content-Type': 'application/json' }, body: JSON.stringify(/* 请求体 */) });
- 前端先从Cookie中读取名为
2. 针对非浏览器客户端:精准禁用CSRF
如果你的API仅供移动端、后端服务等非浏览器客户端调用(这类场景不存在CSRF攻击风险),可以仅对API路径禁用CSRF,保留其他路径的防护:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(HttpMethod.GET,"/topics*").hasAnyRole("USER","ADMIN") .antMatchers(HttpMethod.POST,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.PUT,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.DELETE,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.GET,"/hellos*").hasRole("ADMIN") .and() .formLogin().and().httpBasic() // 仅对指定API路径禁用CSRF .and().csrf(csrf -> csrf.ignoringAntMatchers("/topics*", "/hellos*")); return http.build(); }
3. 无状态认证场景(如JWT):调整CSRF策略
如果你的系统采用JWT这类无状态认证方式(Token存储在前端本地而非Cookie),CSRF攻击风险极低,可以结合配置优化:
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() // 权限配置不变 .antMatchers(HttpMethod.GET,"/topics*").hasAnyRole("USER","ADMIN") .antMatchers(HttpMethod.POST,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.PUT,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.DELETE,"/topics*").hasRole("ADMIN") .antMatchers(HttpMethod.GET,"/hellos*").hasRole("ADMIN") .and() .formLogin().and().httpBasic() // 配置CSRF Token存储方式,同时对JWT认证的API路径禁用CSRF .and().csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringAntMatchers("/api/**") // 替换为你的JWT API路径 ); return http.build(); }
内容的提问来源于stack exchange,提问作者Ravi teja thumnoori
相关产品推荐
相关产品推荐

