Spring+AngularJS表单提交POST请求无法传递CSRF令牌问题求助
你的核心问题很明确:模板里的Spring EL表达式 ${_csrf.parameterName} 和 ${_csrf.token} 没有被正确解析,导致表单提交时传递的是字符串字面量而非实际的CSRF参数名和令牌值,所以Spring Security的CsrfFilter判定令牌无效,返回403 Forbidden。
下面是针对性的解决思路:
1. 适配模板引擎的语法规则
你当前的表单是纯HTML写法,但Spring EL表达式 ${} 需要对应的模板引擎来解析,不同引擎的语法要求不一样:
如果用Thymeleaf(Spring Boot默认推荐):需要把隐藏域改成Thymeleaf的属性绑定方式,让引擎自动渲染出正确的参数名和令牌:
<form id="w0" class="js_form_output" action="/output" method="post"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> <button type="submit" class="button button_request">Apply now</button> </form>同时确保页面文件后缀是
.html,且项目已引入Thymeleaf依赖(Spring Boot默认已包含)。如果用JSP:需要将页面后缀改为
.jsp,并且在页面顶部添加指令开启EL表达式支持:<%@ page isELIgnored="false" %>这样JSP容器才会解析
${_csrf.parameterName}和${_csrf.token}为实际值。
2. 手动从Cookie中获取令牌(备选方案)
因为你配置了 CookieCsrfTokenRepository.withHttpOnlyFalse(),CSRF令牌会存在名为 XSRF-TOKEN 的Cookie中(默认名称),可以通过JavaScript读取Cookie并动态设置隐藏域的值:
// 工具函数:根据Cookie名获取对应值 function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // 页面加载时自动设置CSRF令牌 window.addEventListener('load', () => { const csrfToken = getCookie('XSRF-TOKEN'); // Spring Security默认的CSRF参数名是_csrf const csrfParam = '_csrf'; const csrfInput = document.querySelector('input[name="${_csrf.parameterName}"]'); if (csrfInput) { csrfInput.name = csrfParam; csrfInput.value = csrfToken; } });
这种方式不依赖模板引擎,但不如引擎解析的方式简洁可靠。
3. 验证Spring Security配置的正确性
再次确认你的安全配置确实启用了正确的CSRF令牌仓库:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); // 其他安全配置... } }
withHttpOnlyFalse() 是关键配置——它允许前端JavaScript访问存储CSRF令牌的Cookie,这也是AngularJS能正常发起请求的原因(AngularJS会自动读取该Cookie并在请求头中携带 X-XSRF-TOKEN)。
内容的提问来源于stack exchange,提问作者vipme

