Wenn der protect_from_forgery
Option wird in application_controller erwähnt, dann kann ich mich anmelden und alle GET-Anfragen ausführen, aber bei allerersten POST request Rails setzt die Sitzung zurück, die sich anmeldet mich raus.
Ich drehte den protect_from_forgery
Option vorübergehend deaktiviert, möchte sie jedoch mit Angular.js verwenden. Gibt es eine Möglichkeit, das zu tun?
Ich denke, dass das Lesen des CSRF-Werts von DOM keine gute Lösung ist, sondern nur eine Problemumgehung.
Hier ist ein Dokument aus der offiziellen AngularJS-Website http://docs.angularjs.org/api/ng.$http :
Da nur JavaScript, das auf Ihrer Domain ausgeführt wird, das Cookie lesen konnte, kann Ihr Server sicher sein, dass die XHR von JavaScript stammt, das auf Ihrer Domain ausgeführt wird.
Um dies nutzen zu können (CSRF-Schutz), muss Ihr Server bei der ersten HTTP-GET-Anforderung ein Token in einem von JavaScript lesbaren Sitzungscookie namens XSRF-TOKEN festlegen. Bei nachfolgenden Nicht-GET-Anforderungen kann der Server überprüfen, ob das Cookie mit dem X-XSRF-TOKEN-HTTP-Header übereinstimmt
Hier ist meine Lösung basierend auf diesen Anweisungen:
Setzen Sie zuerst den 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
Dann sollten wir das Token bei jeder Nicht-GET-Anforderung überprüfen.
Da Rails bereits mit der ähnlichen Methode erstellt wurde, können wir sie einfach überschreiben, um unsere Logik anzufügen:
# 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
Wenn Sie die Standardeinstellung Rails CSRF-Schutz (<%= csrf_meta_tags %>
) können Sie Ihr Angular Modul wie folgt konfigurieren:
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
Oder wenn Sie kein CoffeeScript verwenden (was?):
myAngularApp.config([
"$httpProvider", function($httpProvider) {
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
Wenn Sie es vorziehen, können Sie den Header nur bei Nicht-GET-Anforderungen mit etwa den folgenden Angaben senden:
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
]
Lesen Sie auch HungYuHeis Antwort , das alle Basen auf dem Server und nicht auf dem Client abdeckt.
Der Edelstein angle_Rails_csrf fügt allen Ihren Controllern automatisch Unterstützung für das in HungYuHeis Antwort beschriebene Muster hinzu:
# Gemfile
gem 'angular_Rails_csrf'
Die Antwort, bei der alle vorherigen Antworten zusammengeführt werden und die davon abhängt, dass Sie den Authentifizierungs-Gem Devise
verwenden.
Fügen Sie zunächst den Edelstein hinzu:
gem 'angular_Rails_csrf'
Fügen Sie als Nächstes den Block rescue_from
In application_controller.rb hinzu:
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
Und zum Schluss fügen Sie das Interceptor-Modul hinzu angular App.
# 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')
Ich sah die anderen Antworten und fand sie großartig und gut durchdacht. Ich habe meine Rails - App zwar mit einer meiner Meinung nach einfacheren Lösung zum Laufen gebracht, also dachte ich, ich würde sie teilen. In meiner Rails App war dies standardmäßig enthalten.
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
Ich habe die Kommentare gelesen und es schien so, als ob ich angular verwenden und den csrf-Fehler vermeiden möchte. Ich habe es in dieses geändert,
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
Und jetzt funktioniert es! Ich sehe keinen Grund, warum dies nicht funktionieren sollte, aber ich würde gerne Einblicke von anderen Postern erhalten.
Ich habe einen sehr schnellen Hack gefunden. Ich musste nur Folgendes tun:
ein. Meiner Ansicht nach initialisiere ich ein $scope
Variable, die das Token enthält, sagen wir vor dem Formular oder noch besser bei der Controller-Initialisierung:
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. Bevor ich meinen neuen Eintrag in meinem AngularJS-Controller speichere, füge ich den Token dem Hash hinzu:
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.Push(entry)
$scope.newEntry = {}
Es muss nichts mehr getan werden.
Ich habe den Inhalt aus der Antwort von HungYuHei in meiner Bewerbung verwendet. Ich stellte jedoch fest, dass ich einige zusätzliche Probleme hatte, einige wegen der Verwendung von Devise für die Authentifizierung und andere wegen der Standardeinstellung, die ich mit meiner Anwendung hatte:
protect_from_forgery with: :exception
Ich nehme das verwandte Stapelüberlauf-Frage und die Antworten dort zur Kenntnis und schrieb ein viel ausführlicheres Blog-Beitrag , das die verschiedenen Überlegungen zusammenfasst. Die Teile dieser Lösung, die hier relevant sind, sind im Anwendungscontroller:
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;
}
};
}
);
Es funktioniert auf der eckigen Seite!