Application_controllerにprotect_from_forgery
オプションが記載されている場合、ログインしてGETリクエストを実行できますが、最初にPOSTリクエストRailsがセッションをリセットし、それによりログが記録されますでる。
protect_from_forgery
オプションを一時的にオフにしましたが、Angular.jsで使用したいと思います。それを行う方法はありますか?
DOMからCSRF値を読み取ることは良い解決策ではなく、単なる回避策だと思います。
次に、angularJS公式ウェブサイト http://docs.angularjs.org/api/ng.$http からのドキュメントを示します。
ドメインで実行されるJavaScriptのみがCookieを読み取ることができるため、XHRがドメインで実行されているJavaScriptからのものであることをサーバーに保証できます。
これを活用するには(CSRF保護)、サーバーは、最初のHTTP GET要求でXSRF-TOKENと呼ばれるJavaScriptで読み取り可能なセッションCookieにトークンを設定する必要があります。後続の非GET要求で、サーバーはCookieがX-XSRF-TOKEN HTTPヘッダーと一致することを確認できます
これらの指示に基づいた私のソリューションは次のとおりです。
まず、Cookieを設定します。
# app/controllers/application_controller.rb
# Turn on request forgery protection
protect_from_forgery
after_action :set_csrf_cookie
def set_csrf_cookie
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
次に、GET以外のすべてのリクエストでトークンを検証する必要があります。
Railsは既に同様のメソッドで構築されているため、単純にオーバーライドしてロジックを追加できます。
# app/controllers/application_controller.rb
protected
# In Rails 4.2 and above
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
# In Rails 4.1 and below
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
デフォルトのRails CSRF保護(<%= csrf_meta_tags %>
)を使用している場合、Angularモジュールを次のように構成できます。
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
または、CoffeeScriptを使用していない場合(何!?):
myAngularApp.config([
"$httpProvider", function($httpProvider) {
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
必要に応じて、次のような非GET要求でのみヘッダーを送信できます。
myAngularApp.config ["$httpProvider", ($httpProvider) ->
csrfToken = $('meta[name=csrf-token]').attr('content')
$httpProvider.defaults.headers.post['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.put['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.patch['X-CSRF-Token'] = csrfToken
$httpProvider.defaults.headers.delete['X-CSRF-Token'] = csrfToken
]
また、 HungYuHeiの答え も確認してください。これは、クライアントではなくサーバー上のすべてのベースをカバーします。
angular_Rails_csrf gemは、 HungYuHei's answer で説明されているパターンのサポートをすべてのコントローラーに自動的に追加します。
# Gemfile
gem 'angular_Rails_csrf'
以前のすべての回答をマージする回答は、Devise
認証gemを使用していることを前提としています。
まず、gemを追加します。
gem 'angular_Rails_csrf'
次に、rescue_from
ブロックをapplication_controller.rbに追加します。
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken do |exception|
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
render text: 'Invalid authenticity token', status: :unprocessable_entity
end
最後に、インターセプターモジュールをangularアプリに追加します。
# coffee script
app.factory 'csrfInterceptor', ['$q', '$injector', ($q, $injector) ->
responseError: (rejection) ->
if rejection.status == 422 && rejection.data == 'Invalid authenticity token'
deferred = $q.defer()
successCallback = (resp) ->
deferred.resolve(resp)
errorCallback = (resp) ->
deferred.reject(resp)
$http = $http || $injector.get('$http')
$http(rejection.config).then(successCallback, errorCallback)
return deferred.promise
$q.reject(rejection)
]
app.config ($httpProvider) ->
$httpProvider.interceptors.unshift('csrfInterceptor')
私は他の答えを見て、それらは素晴らしいと考え抜かれたと思いました。私はRailsアプリを動作させましたが、もっと簡単なソリューションだと思ったので、共有したいと思いました。私のRailsアプリにはこれがデフォルトで付属しており、
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
コメントを読みましたが、angularを使用してcsrfエラーを回避したいようです。これに変更しました
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session
end
そして今、それは動作します!これが機能しない理由はわかりませんが、他のポスターからいくつかの洞察を聞きたいです。
これに非常に簡単なハックを見つけました。私がしなければならなかったのは次のことです:
a。私の見解では、トークンを含む$scope
変数を初期化します。たとえば、フォームの前、またはコントローラーの初期化でさらに良いことです。
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b。 AngularJSコントローラーで、新しいエントリを保存する前に、トークンをハッシュに追加します。
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.Push(entry)
$scope.newEntry = {}
これ以上何もする必要はありません。
HungYuHeiの回答のコンテンツをアプリケーションで使用しました。ただし、認証にDeviseを使用しているためと、アプリケーションで取得したデフォルトのために、いくつかの追加の問題に対処していることがわかりました。
protect_from_forgery with: :exception
関連する スタックオーバーフローの質問とその回答 に注意し、さらに詳細な ブログ投稿 を書いて、さまざまな考慮事項を要約します。ここで関連するそのソリューションの部分は、アプリケーションコントローラーにあります。
protect_from_forgery with: :exception
after_filter :set_csrf_cookie_for_ng
def set_csrf_cookie_for_ng
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
rescue_from ActionController::InvalidAuthenticityToken do |exception|
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
render :error => 'Invalid authenticity token', {:status => :unprocessable_entity}
end
protected
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
angular
.module('corsInterceptor', ['ngCookies'])
.factory(
'corsInterceptor',
function ($cookies) {
return {
request: function(config) {
config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN');
return config;
}
};
}
);
これは、angularjs側で機能しています!