1

ShopifyサイトからShopifyアプリとしてインストールされているRailsアプリへのクロスドメインリクエストを行うのに問題があります。Can't verify CSRF token authenticityタイトルに記載されているように、問題は、関連するCSRFトークンを含むRailsアプリから返されたフォームからリクエストを送信していることをサーバーが警告することです。リクエストはjQueryのajaxメソッドを使用して実行され、プリフライトOPTIONSリクエストはrack-corsによって処理されます。

この回答で提案されているように、ヘッダーにX-CSRF-Tokenを含めました。私の投稿リクエストはフォームから行われているため、ここでは質問に回答していません。この質問をして確認したところ、オプションのリクエスト(この質問で言及)は実際に処理されています。私はしばらくこれに固執していて、少し読んでいます。

コードスニペットごとにプロセスをウォークスルーしてみます。おそらく、この投稿を書き終えるまでに、私の問題に対する答えを見つけているでしょう(それが起こった場合、あなたは決してこの段落を読むチャンス)。

これが私のコントローラーからの新しいメソッドとcreateメソッドです。

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

(createメソッドのrespond_toブロックについては疑問ですが、CSRFトークンの検証に失敗することはないと思います。)

私のアプリ内の/AUSController/ indexに、/ AUSController/newからフォームを返すajaxifiedGETリクエストがあります。私の目標は、アプリ内からできるのと同じように、クロスドメインオリジンからすべて同じ呼び出しを行えるようにすることです。現在、GETリクエストは両方で機能するため、「new」フォームを含めることを怠ります。HTMLが最終的にレンダリングされると、form要素は次のようになります。

<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トークンは、上記form_authenticity_tokenの参照の1つで詳しく説明されているへの呼び出しによって生成されます。

次のステップは、2つの場合で異なる方法で実行されます。

私のアプリは、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。少なくとも、それは満足のいくものでしょう。

4

1 に答える 1

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
}

そして今、それは機能します!ありがとう同僚!

于 2013-01-13T20:12:29.303 に答える