如何从AngularJS控制器提交含CSRF Token的数据至REST控制器
解决AngularJS + Spring Security CSRF启用后POST/PUT 405问题
首先,咱们先理清问题根源:启用CSRF防护后,Spring Security要求所有**修改类请求(POST/PUT/DELETE)**必须携带CSRF Token,同时你的跨域(CORS)配置和AngularJS的请求处理没有跟上这个要求,导致浏览器预检请求(OPTIONS)处理失败,最终触发405错误。下面一步步给你解决:
第一步:调整Spring端的CORS与CSRF配置
1. 修复CORS过滤器(处理OPTIONS请求与跨域头)
你的CORSFilter需要做三个关键修改:允许携带Cookie、放行CSRF相关请求头、正确处理OPTIONS预检请求(浏览器会先发这个请求确认跨域权限):
public class CORSFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; // 不要用*,带Cookie的跨域请求必须指定具体源,这里动态获取请求的Origin response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); // 新增X-XSRF-TOKEN到允许的请求头列表 response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type, X-XSRF-TOKEN"); // 允许携带Cookie(必须开启,否则前端拿不到CSRF Token的Cookie) response.setHeader("Access-Control-Allow-Credentials", "true"); // 直接响应OPTIONS请求,不用走后续过滤器链 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } public void init(FilterConfig filterConfig) {} public void destroy() {} }
2. 配置Spring Security的CSRF Token存储
默认Spring Security会把CSRF Token存在XSRF-TOKEN Cookie里,但默认是HttpOnly=true(前端JS读不到),所以要修改配置让前端能读取这个Cookie:
在SecurityConfiguration的configure(HttpSecurity http)方法里,更新CSRF部分:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/restUser/**").access("hasRole('ADMIN')") .and().formLogin() .loginPage("/login").loginProcessingUrl("/login") .usernameParameter("usernameId").passwordParameter("password") .and().rememberMe().rememberMeParameter("remember-me") .tokenRepository(tokenRepository).tokenValiditySeconds(86400) .and().csrf() // 配置CSRF Token Cookie允许前端JS读取 .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and().exceptionHandling().accessDeniedPage("/Access_Denied"); }
第二步:AngularJS端配置自动携带CSRF Token
AngularJS其实默认自带CSRF Token自动处理逻辑,但需要确保几个配置正确:
在你的AngularJS模块的config里添加以下配置:
angular.module('yourAppName', []) .config(['$httpProvider', function($httpProvider) { // 告诉AngularJS从哪个Cookie读CSRF Token(和Spring配置的一致) $httpProvider.defaults.xsrfCookieName = 'XSRF-TOKEN'; // 告诉AngularJS把Token放到哪个请求头里(和Spring期望的一致) $httpProvider.defaults.xsrfHeaderName = 'X-XSRF-TOKEN'; // 开启携带Cookie,这样CSRF Token的Cookie才会被发送到后端 $httpProvider.defaults.withCredentials = true; }]);
控制器里正常提交请求即可
配置好后,你在控制器里用$http.post/$http.put的时候,AngularJS会自动帮你把CSRF Token放到请求头里,不需要手动处理:
angular.module('yourAppName') .controller('UserController', ['$http', function($http) { // 创建用户示例 this.createUser = function(userData) { $http.post('/restUser', userData) .then(function(res) { console.log('用户创建成功!'); }, function(err) { console.error('创建失败:', err); }); }; // 更新用户示例 this.updateUser = function(userId, userData) { $http.put(`/restUser/${userId}`, userData) .then(function(res) { console.log('用户更新成功!'); }, function(err) { console.error('更新失败:', err); }); }; }]);
为什么之前会报405?
- 启用CSRF后,浏览器会先发OPTIONS预检请求,你的旧CORS过滤器没有直接响应OPTIONS,导致请求走到Spring Security的过滤器链,触发权限拦截。
- 跨域配置没有允许携带Cookie和CSRF相关请求头,AngularJS无法读取和发送CSRF Token,后端验证失败后返回405。
内容的提问来源于stack exchange,提问作者rob
相关产品推荐
相关产品推荐

