我有一个单页应用程序,使用CORS对另一个域进行身份验证。所有请求都是JSON请求。
我的应用可以验证正常,可以让GET请求正常。身份验证使用token_authenticatable。即所有请求附加'?auth_token = whatever'
所以,我的实际问题是,当我尝试执行PUT请求时,我在rails日志中获得警告:无法验证CSRF令牌真实性消息以及 CanCan :: AccessDenied(您无权访问此页面。)例外。
只需将skip_before_filter :verify_authenticity_token
添加到rails控制器即可解决问题。
因此我只能断定我的ajax请求发送的是无效或空的csrf_token。
我真的不明白这是怎么回事,因为我相信我正确地为每个ajax请求正确发送了X-CSRF-Token标头。
基本上,我的应用验证并且Devise发回一个auth_token和一个csrf_token:
render :status => 200, :json => {
:auth_token => @user.authentication_token,
:csrf_token => form_authenticity_token
}
然后我将这些令牌存储在我的ajax应用程序中,并在jQuery中使用ajaxSend
,设置它以便jQuery为每个请求传递这些令牌:
initialize: ->
@bindTo $(document), 'ajaxSend', @appendTokensToRequest
appendTokensToRequest: (event, jqXHR, options) ->
if not @authToken? then return
if @csrfToken?
jqXHR.setRequestHeader 'X-CSRF-Token', @csrfToken
if options.type is 'POST'
options.data = options.data + (if options.data.match(/\=/) then '&' else '') +
$.param auth_token:@authToken
else
options.url = options.url + (if options.url.match(/\?/) then '&' else '?') +
$.param auth_token:@authToken
然后,我可以在Chrome网络标签中看到,每个GET请求都会发送auth_token
参数,以及X-CSRF-Token
标题。
然而,在PUT请求中它似乎没有工作。
我的理论是CORS正在填补空白。如果您发出了CORS请求,那么您的浏览器实际上会首先发出附加 OPTIONS请求,以检查您是否有权访问此资源。
我怀疑OPTIONS请求没有传递X-CSRF-Token标头,因此rails会立即使rails端的csrf_token无效。然后,当jQuery发出实际的PUT请求时,它传递的csrf_token不再有效。
这可能是问题吗?
我能做些什么来证明这一点? Chrome似乎没有向我显示网络标签中的OPTIONS请求,以帮助我调试问题。
这不是一个重大问题,因为我可以关闭CSRF。但我想知道它为什么不起作用。
答案 0 :(得分:1)
我认为你需要处理OPTIONS请求,它应该响应允许CORS请求的各种头,IIRC它们是access-control-allow-method,access-control-allow-origin和access - 控制 - 允许报头。由于OPTIONS请求失败,因此可能没有发生PUT请求。
答案 1 :(得分:1)
我刚遇到同样的问题。问题是无法在CORS中发送using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
Root root = new Root()
{
baseClass = new List<BaseClass>(){
new InheritorClass1(){ name = "class1"},
new InheritorClass2(){ name = "class2"}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Root));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, root);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(Root));
XmlTextReader reader = new XmlTextReader(FILENAME);
Root newRoot = (Root)xs.Deserialize(reader);
}
}
[XmlRoot("Root")]
public class Root
{
[XmlElement("BaseClass")]
public List<BaseClass> baseClass { get; set; }
}
[XmlRoot("BaseClass")]
[XmlInclude(typeof(InheritorClass1))]
[XmlInclude(typeof(InheritorClass2))]
public class BaseClass
{
[XmlElement("name")]
public string name { get; set; }
}
[XmlRoot("InheritorClass1")]
public class InheritorClass1 : BaseClass
{
}
[XmlRoot("InheritorClass2")]
public class InheritorClass2 : BaseClass
{
}
}
cookie。因此,当Rails尝试验证令牌时,_session_id
为session[:_csrf_token]
,Rails会在比较之前生成新的令牌。
要解决此问题,您需要在CORS中启用cookie发送。这是Mozilla Developer Network reference。服务器和客户端都需要工作才能使其正常工作。
<强>客户端强> - 请参阅您的客户技术文档。
服务器强>
- 在对预检(HTTP OPTIONS)调用的响应中将标题null
设置为Access-Control-Allow-Credentials
(字符串)。
答案 2 :(得分:0)
在Rails中,每个表单提交都需要CSRF令牌真实性。 它用于安全地提交表单。 当我们打开应用程序时,CSRF令牌(每次)将在rails中新创建。 如果CSRF令牌未通过我们的控制器内部,则此警告将显示。 我们需要在所有表单提交中传递此令牌。