2013-01-09 50 views
1

我无法从我的shopify站点向我的rails应用程序发出跨域请求,该应用程序作为shopify应用程序安装。正如标题所述,问题在于我的服务器警告我它从我的rails应用程序返回的表单发出请求,其中包含相关的CSRF令牌。该请求使用jQuery的ajax方法完成,并且预检OPTIONS请求正在由rack-cors处理。警告:无法验证CSRF令牌的真实性

我在我的头文件中包含了X-CSRF-Token,如this answer中所建议的那样。我的帖子请求正在从表单中提取,所以我的问题未回答here。选项要求(在this question中提到)确实正在处理中,因为我刚刚通过询问this question进行了确认。我一直坚持这一点,并做了一些阅读。

我打算尝试通过代码片段来浏览流程代码片段,也许在我完成写这篇文章的时候,我会发现我的问题的答案(如果发生这种情况,那么你赢了'没有机会阅读本段)。

以下是我的控制器中的新建和创建方法。

class AustraliaPostApiConnectionsController < ApplicationController 

    # GET /australia_post_api_connections/new 
    # GET /australia_post_api_connections/new.json 
    def new 
    # initializing variables 

    respond_to do |format| 
     puts "---------------About to format--------------------" 
     format.html { render layout: false } # new.html.erb 
     format.json { render json: @australia_post_api_connection } 
    end 
    end 

    # POST /australia_post_api_connections 
    # POST /australia_post_api_connections.json 
    def create 

    @australia_post_api_connection = AustraliaPostApiConnection.new(params[:australia_post_api_connection]) 

    respond_to do |format| 
     if @australia_post_api_connection.save 

     format.js { render layout: false } 
     else 

     format.js { render layout: false } 
     end 
    end 
    end 
end 

(我想知道在创建方法的respond_to代码块,但我不认为这会导致CSRF令牌验证失败。)

在我的应用程序,在/ AUSController /指数,我有一个Ajax化的GET请求,从/ AUSController/new返回表单。我的目标是能够从我的应用内完成跨域域的所有相同的调用。现在,GET请求适用于两者,因此我将忽略包含“新”表单。当HTML被最终呈现,表单元件具有以下内容:通过向form_authenticity_token一个呼叫产生

<form method="post" id="new_australia_post_api_connection" data-remote="true" class="new_australia_post_api_connection" action="http://localhost:3000/australia_post_api_connections" accept-charset="UTF-8"> 

<!-- a bunch more fields here --> 

    <div class="field hidden"> 
     <input type="hidden" value="the_csrf_token" name="authenticity_token" id="tokentag"> 
    </div> 
    </div> 
</div> 
</form> 

的CSRF令牌作为在引用mentioned above之一详述。

下一步是在两种情况下做不同的:

我的应用程序成功地在一个Ajax请求返回新形式的商店。我已经在应用程序中测试了这一点,即通过/ controller/index对/ controller/new进行ajax调用,然后提交表单。这像一个魅力。这是从一个成功的POST我的应用程序中返回的JS如下:

/ this is rendered when someone hits "calculate" and whenever the country select changes 
:plain 
    $("#shipping-prices").html("#{escape_javascript(render(:partial => 'calculations', :object => @australia_post_api_connection))}") 

这使得下面的部分,

= form_tag "/shipping_calculations", :method => "get" do 

    = label_tag :shipping_type 
    %br 
    - @service_list.each_with_index do |service, index| 
    - checked = true if index == 0 
    = radio_button_tag(:shipping_type, service[:code], checked) 
    = label_tag(:"shipping_type_#{service[:code]}", service[:name]) 
    = " -- $#{service[:price]}" 
    %br 

当我把它从同一个域,request.header包含以下内容:

HTTP_X_CSRF_TOKEN 
the_token_I_expect= 

rack.session 
{ 
    "session_id"=>"db90f199f65554c70a6922d3bd2b7e61", 
    "return_to"=>"/", 
    "_csrf_token"=>"the_token_I_expect=", 
    "shopify"=>#<ShopifyAPI::Session:0x000000063083c8 @url="some-shop.myshopify.com", @token="some_token"> 
} 

而且HTML呈现并很好地显示。

但是,从跨域来源看,事情变得更加复杂。这就是CORS和CSRF令牌和路由以及所有这些小细节开始蔓延的地方。特别是,当我进行ajax调用时,我使用以下脚本(它不在我的rails应用程序中,它驻留在跨域服务器上)。这个ajax请求的动作通过GET请求的回调函数附加到提交按钮上,为了完成,我已经包含了GET请求。

<script> 

    var host = "http://localhost:3000/" 
    var action = "australia_post_api_connections" 

    console.log("start") 
    $.ajax({ 
    url: host + action, 
    type: "GET", 
    data: { weight: 20 }, 
    crossDomain: true, 
    xhrFields: { 
     withCredentials: true 
    }, 
    success: function(data) { 
     console.log("success"); 
     $('#shipping-calculator').html(data); 

     $('#new_australia_post_api_connection') 
     .attr("action", host + action); 

    $('.error').hide(); 

    $(".actions > input").click(function() { 
     console.log("click") 
     // validate and process form here 
     $('.error').hide(); 

     var to_postcode = $("input#australia_post_api_connection_to_postcode").val(); 

     // client side validation 
     if (to_postcode === "") { 
     $("#postcode > .error").show(); 
     $("input#australia_post_api_connection_to_postcode").focus(); 
     return false; 
     } 

     tokentag = $('#tokentag').val() 

     var dataHash = { 
     to_postcode: to_postcode, 
     authenticity_token: tokentag // included based on an SO answer 
     } 

     // included based on an SO answer 
     $.ajaxSetup({ 
     beforeSend: function(xhr) { 
      xhr.setRequestHeader('X-CSRF-TOKEN', tokentag); 
     } 
     }); 

     $.ajax({ 
     type: "POST", 
     url: host + action, 
     data: dataHash, 
     success: function(data) { 
      $('#shipping-prices').html(data); 
     } 
     }).fail(function() { console.log("fail") }) 
     .always(function() { console.log("always") }) 
     .complete(function() { console.log("complete") }); 
     return false; 

    }); 

    } 
    }).fail(function() { console.log("fail") }) 
    .always(function() { console.log("always") }) 
    .complete(function() { console.log("complete") }); 

    $(function() { 
    }); 

</script> 

然而,当我把它从这个远程位置(Shopify的远处的山坡上),我发现在我的请求头下面,

HTTP_X_CSRF_TOKEN 
the_token_I_expect= 

rack.session 
{ } 

而且我收到了非常不愉快的NetworkError: 500 Internal Server Error,而不是该200 OK!,我想...在服务器端,我们发现日志抱怨说,

Started POST "/australia_post_api_connections" for 127.0.0.1 at 2013-01-08 19:20:25 -0800 
Processing by AustraliaPostApiConnectionsController#create as */* 
    Parameters: {"weight"=>"20", "to_postcode"=>"3000", "from_postcode"=>"3000", "country_code"=>"AUS", "height"=>"16", "width"=>"16", "length"=>"16", "authenticity_token"=>"the_token_I_expect="} 
WARNING: Can't verify CSRF token authenticity 
Completed 500 Internal Server Error in 6350ms 

AustraliaPostApiConnection::InvalidError (["From postcode can't be blank", "The following errors were returned by the Australia Post API", "Please enter Country code.", "Length can't be blank", "Length is not a number", "Height can't be blank", "Height is not a number", "Width can't be blank", "Width is not a number", "Weight can't be blank", "Weight is not a number"]): 
    app/models/australia_post_api_connection.rb:78:in `save' 

缺乏一个rack.session的似乎像我的痛苦的原因可疑...但我一直没能找到一个令人满意的答案。

最后,我已经看到适合包括我的机架设置,以防万一它有用。

# configuration for allowing some servers to access the aus api connection 
config.middleware.use Rack::Cors do 
    allow do 
    origins 'some-shop.myshopify.com' 
    resource '/australia_post_api_connections', 
     :headers => ['Origin', 'Accept', 'Content-Type', 'X-CSRF-Token'], 
     :methods => [:get, :post] 
    end 
end 

非常感谢您阅读所有这些内容。我希望答案与那个空的rack.session有关。至少,这将是令人满意的。

+0

我只是读了这个:http://stackoverflow.com/questions/12630231/how-do-cors-and-access-control-allow-headers-work,我想知道那个答案是什么意思。 – Ziggy

回答

2

那么我的一个同事想出了它。问题是,我发送的结果与我在控制器中期望的散列结构不一样。

在我控制我实例化一个新的API连接如下,

AustraliaPostApiConnection.new(params[:australia_post_api_connection])

我找params[:australia_post_api_connection],但在我的数据散列,它看起来像这样的索引,

var dataHash = { 
    to_postcode: to_postcode, 
    authenticity_token: tokentag // included based on an SO answer 
} 

为了解决这个问题,我改变了JS文件包含

var dataHash = { 
    to_postcode: to_postcode, 
} 

var params = { 
    australia_post_api_connection: dataHash, 
    authenticity_token: tokentag // included based on an SO answer 
} 

现在它的工作原理!感谢同事!

相关问题