UnityWebRequest对接Django时CSRF令牌缺失或错误问题求助
解决UnityWebRequest对接Django后端的CSRF令牌验证失败问题
我之前在Unity对接Django登录接口时也碰到过一模一样的问题,咱们从几个关键节点来排查和修正:
1. 表单提交格式不匹配(最可能的根源)
你当前用List<IMultipartFormSection>构建表单数据,这会让Unity发送multipart/form-data类型的请求,但Django默认的登录视图期望接收的是application/x-www-form-urlencoded格式的表单数据。这种格式不匹配会导致Django无法正确解析表单中的csrfmiddlewaretoken字段,直接触发CSRF验证失败。
修正方式:改用WWWForm来构建表单数据,Unity会自动设置正确的Content-Type头:
// 替换原来的List<IMultipartFormSection> WWWForm formData = new WWWForm(); formData.AddField("username", "fake"); formData.AddField("password", "notpass"); formData.AddField("csrfmiddlewaretoken", csrfMiddlewareToken); UnityWebRequest doLogin = UnityWebRequest.Post("https://dividedsky.herokuapp.com/accounts/login/", formData);
2. Cookie传递不完整
你的代码只提取了csrftoken,但Django返回登录页时可能还设置了其他Cookie(比如sessionid),这些Cookie需要在POST请求中完整传递。另外直接用SetRequestHeader("cookie", ...)会覆盖原有Cookie,建议先提取完整的Cookie键值对再设置:
修正Cookie提取和设置逻辑:
// 获取完整的Set-Cookie字符串 string fullSetCookie = loginPage.GetResponseHeader("set-cookie"); // 提取所有key=value格式的Cookie内容 Regex rxAllCookies = new Regex(@"([^=]+=[^;]+);"); MatchCollection cookieMatches = rxAllCookies.Matches(fullSetCookie); StringBuilder cookieHeader = new StringBuilder(); foreach (Match match in cookieMatches) { if (cookieHeader.Length > 0) cookieHeader.Append("; "); cookieHeader.Append(match.Groups[1].Value); } // 设置完整的Cookie请求头 doLogin.SetRequestHeader("cookie", cookieHeader.ToString());
3. 补充X-CSRFToken请求头(备选方案)
如果表单方式还是不行,可以尝试用Django支持的X-CSRFToken请求头传递令牌,这种方式有时候比表单字段更可靠:
// 从Cookie中提取csrftoken的值,添加到请求头 string csrfToken = cookieHeader.ToString().Split(';')[0].Split('=')[1]; doLogin.SetRequestHeader("X-CSRFToken", csrfToken);
4. 确保Referer头的正确性
你的代码已经设置了Referer头,但要确保它和登录页URL完全一致(包括协议、域名、路径),Django的CSRF验证会严格检查Referer是否属于可信域。
修改后的完整代码示例
UnityWebRequest loginPage = UnityWebRequest.Get("https://dividedsky.herokuapp.com/accounts/login/"); yield return loginPage.SendWebRequest(); if(loginPage.isNetworkError || loginPage.isHttpError) { Debug.Log(loginPage.error); yield break; } // 提取完整的Cookie内容 string fullSetCookie = loginPage.GetResponseHeader("set-cookie"); Regex rxAllCookies = new Regex(@"([^=]+=[^;]+);"); MatchCollection cookieMatches = rxAllCookies.Matches(fullSetCookie); StringBuilder cookieHeader = new StringBuilder(); foreach (Match match in cookieMatches) { if (cookieHeader.Length > 0) cookieHeader.Append("; "); cookieHeader.Append(match.Groups[1].Value); } // 提取页面中的csrfmiddlewaretoken string loginPageHtml = loginPage.downloadHandler.text; Regex rxMiddleware = new Regex("name='csrfmiddlewaretoken' value='(?<csrf_token>.{64})'"); MatchCollection middlewareMatches = rxMiddleware.Matches(loginPageHtml); string csrfMiddlewareToken = middlewareMatches[0].Groups["csrf_token"].Value; // 构建正确格式的表单数据 WWWForm formData = new WWWForm(); formData.AddField("username", "fake"); formData.AddField("password", "notpass"); formData.AddField("csrfmiddlewaretoken", csrfMiddlewareToken); // 发起登录请求 UnityWebRequest doLogin = UnityWebRequest.Post("https://dividedsky.herokuapp.com/accounts/login/", formData); doLogin.SetRequestHeader("referer", "https://dividedsky.herokuapp.com/accounts/login/"); doLogin.SetRequestHeader("cookie", cookieHeader.ToString()); // 可选:添加X-CSRFToken头增强兼容性 string csrfToken = cookieHeader.ToString().Split(';')[0].Split('=')[1]; doLogin.SetRequestHeader("X-CSRFToken", csrfToken); yield return doLogin.SendWebRequest(); Debug.Log(doLogin.downloadHandler.text);
额外注意点:
- 确保你提取的
csrfmiddlewaretoken和csrftokenCookie值是匹配的(Django要求二者对应) - 如果你的Django后端配置了
CSRF_TRUSTED_ORIGINS,要确保Unity请求的域名在这个列表内
内容的提问来源于stack exchange,提问作者Mark Miller
相关产品推荐
相关产品推荐

