Spring Security下Ajax POST请求失败(GET正常),_csrf处理后参数为空
解决Spring Security下jQuery Ajax POST数据为null的问题
兄弟,太懂你折腾半天的痛苦了——你之前把CSRF令牌塞URL里,虽然绕开了Spring Security的拦截,但数据全变成null,核心问题是CSRF令牌的传递方式不对,再加上可能的参数绑定小问题,下面一步步给你搞定:
一、别再把CSRF令牌放URL里了!正确的传递姿势
Spring Security推荐把CSRF令牌放在请求头或者请求体里,URL传令牌不仅有安全风险,还容易干扰参数解析:
方式1:请求头传递(最推荐)
先在页面里通过模板引擎(比如Thymeleaf/JSP)渲染出CSRF的令牌和对应的请求头名称:
<!-- Thymeleaf写法,JSP同理,用${_csrf.token}即可 --> <meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/>
然后在jQuery Ajax里设置请求头:
// 从meta标签里拿令牌和头信息 var csrfToken = $("meta[name='_csrf']").attr("content"); var csrfHeader = $("meta[name='_csrf_header']").attr("content"); $.ajax({ type: "POST", url: "/your-api-path", // 这里不用加csrf参数! beforeSend: function(xhr) { // 把CSRF令牌放到请求头里 xhr.setRequestHeader(csrfHeader, csrfToken); }, data: { username: "testUser", email: "test@example.com" }, success: function(res) { console.log("请求成了!返回数据:", res); }, error: function(xhr, status, err) { console.error("凉了:", err); } });
方式2:请求体一起传
如果不想用请求头,也可以把令牌和业务数据打包在data里:
var csrfToken = $("meta[name='_csrf']").attr("content"); $.ajax({ type: "POST", url: "/your-api-path", data: { _csrf: csrfToken, // 直接把令牌放数据里 username: "testUser", email: "test@example.com" }, success: function(res) { console.log("搞定!"); } });
二、后端参数绑定要对应,不然数据肯定null
你说数据全是null,大概率是后端接收参数的方式和前端传的不匹配,分两种情况:
情况1:前端传表单格式(默认)
前端默认的contentType是application/x-www-form-urlencoded,后端用@RequestParam接收单个参数,要保证参数名完全一致:
@PostMapping("/your-api-path") public ResponseEntity<String> handleRequest( @RequestParam("username") String username, @RequestParam("email") String email ) { // 这里就能拿到正常的参数了 return ResponseEntity.ok("收到数据:" + username + " | " + email); }
情况2:前端传JSON格式
如果要传JSON,必须手动设置contentType,并且把数据转成JSON字符串:
$.ajax({ type: "POST", url: "/your-api-path", contentType: "application/json", // 必须加这个! beforeSend: function(xhr) { xhr.setRequestHeader(csrfHeader, csrfToken); }, data: JSON.stringify({ // 转成JSON字符串 username: "testUser", email: "test@example.com" }), success: function(res) { console.log("JSON请求成功!"); } });
后端要用@RequestBody配合实体类接收,记得实体类要有对应属性的getter和setter:
// 先定义实体类 public class UserRequest { private String username; private String email; // 必须加getter和setter! public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } // 控制器方法 @PostMapping("/your-api-path") public ResponseEntity<UserRequest> handleRequest( @RequestBody UserRequest request ) { // 这里request里的属性就有值了 return ResponseEntity.ok(request); }
三、踩过的坑给你提个醒
- 静态HTML页面怎么拿CSRF令牌?如果不用模板引擎,得写个简单的后端接口返回令牌信息,比如:
@GetMapping("/csrf-token") public ResponseEntity<Map<String, String>> getCsrfToken(HttpServletRequest request) { CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); Map<String, String> result = new HashMap<>(); result.put("token", token.getToken()); result.put("header", token.getHeaderName()); return ResponseEntity.ok(result); }
然后前端先调这个接口拿令牌,再发POST请求。
- 别漏了
contentType:传JSON的时候一定要加,不然后端解析不了,参数全是null。
内容的提问来源于stack exchange,提问作者rob




