オプションがapplication_controllerに記載されている場合、protect_from_forgery
ログインしてGETリクエストを実行できますが、最初のPOSTリクエストでRailsがセッションをリセットし、ログアウトします.
オプションを一時的にオフにprotect_from_forgery
していましたが、Angular.js で使用したいと考えています。それを行う方法はありますか?
オプションがapplication_controllerに記載されている場合、protect_from_forgery
ログインしてGETリクエストを実行できますが、最初のPOSTリクエストでRailsがセッションをリセットし、ログアウトします.
オプションを一時的にオフにprotect_from_forgery
していましたが、Angular.js で使用したいと考えています。それを行う方法はありますか?
DOM から CSRF 値を読み取ることは良い解決策ではないと思います。これは単なる回避策です。
angularJS の公式 Web サイト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 の回答を必ず確認してください。
以前のすべての回答をマージする回答であり、Devise
認証ジェムを使用していることに依存しています。
まず、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
最後に、interceptor モジュールを 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')
これに対する非常に簡単なハックを見つけました。私がしなければならなかったのは次のことだけです:
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 = {}
これ以上何もする必要はありません。