Si la protect_from_forgery
option est mentionnée dans application_controller, je peux alors me connecter et exécuter toutes les requêtes GET, mais le tout premier POST request Rails réinitialise la session, qui enregistre moi dehors.
J'ai tourné le protect_from_forgery
Option désactivée temporairement, mais vous souhaitez l’utiliser avec Angular.js. Y a-t-il un moyen de faire ça?
Je pense que lire CSRF-value à partir de DOM n'est pas une bonne solution, c'est simplement une solution de contournement.
Voici un document sous forme de document site officiel angularJS http://docs.angularjs.org/api/ng.$http :
Étant donné que seul le code JavaScript utilisé sur votre domaine peut lire le cookie, votre serveur peut être assuré que le XHR provient de JavaScript exécuté sur votre domaine.
Pour tirer parti de cette protection (protection CSRF), votre serveur doit définir un jeton dans un cookie de session lisible en JavaScript appelé XSRF-TOKEN lors de la première requête HTTP GET. Lors de demandes ultérieures non-GET, le serveur peut vérifier que le cookie correspond à l'en-tête HTTP X-XSRF-TOKEN.
Voici ma solution basée sur ces instructions:
Tout d'abord, définissez le 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
Ensuite, nous devrions vérifier le jeton sur chaque demande non-GET.
Depuis Rails a déjà été construit avec la même méthode, nous pouvons simplement le remplacer pour ajouter notre logique:
# 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
Si vous utilisez la valeur par défaut Rails Protection CSRF (<%= csrf_meta_tags %>
), vous pouvez configurer votre module Angular comme ceci:
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
Ou, si vous n'utilisez pas CoffeeScript (quoi !?):
myAngularApp.config([
"$httpProvider", function($httpProvider) {
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
Si vous préférez, vous pouvez envoyer l'en-tête uniquement sur des demandes non-GET avec les éléments suivants:
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
]
Aussi, assurez-vous de vérifier réponse de HungYuHei , qui couvre toutes les bases sur le serveur plutôt que sur le client.
La gemme angular_Rails_csrf ajoute automatiquement la prise en charge du modèle décrit dans réponse de HungYuHei à tous vos contrôleurs:
# Gemfile
gem 'angular_Rails_csrf'
La réponse qui fusionne toutes les réponses précédentes et repose sur le fait que vous utilisez la gem d'authentification Devise
.
Tout d'abord, ajoutez la gemme:
gem 'angular_Rails_csrf'
Ensuite, ajoutez rescue_from
bloc dans 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
Et enfin, ajouter le module intercepteur 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')
J'ai vu les autres réponses et je les ai trouvées géniales et bien pensées. J'ai eu mon Rails fonctionnant avec ce que je pensais être une solution plus simple, donc je pensais partager. Mon Rails est venue avec ce défaut dans son ,
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
J'ai lu les commentaires et il me semblait que c'était ce que je voulais utiliser angular et éviter l'erreur csrf. Je l'ai changé en ceci,
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
Et maintenant ça marche! Je ne vois aucune raison pour que cela ne fonctionne pas, mais j'aimerais entendre un aperçu des autres affiches.
J'ai trouvé un hack très rapide à cela. Tout ce que j'avais à faire est le suivant:
une. À mon avis, j'initialise un $scope
variable qui contient le jeton, disons avant le formulaire, voire mieux à l’initialisation du contrôleur:
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. Dans mon contrôleur AngularJS, avant d’enregistrer ma nouvelle entrée, j’ajoute le jeton au hachage:
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.Push(entry)
$scope.newEntry = {}
Il ne reste plus rien à faire.
J'ai utilisé le contenu de la réponse de HungYuHei dans ma candidature. J'ai toutefois constaté quelques problèmes supplémentaires, certains à cause de mon utilisation de Devise pour l'authentification et d'autres à cause du défaut de ma candidature:
protect_from_forgery with: :exception
Je note le connexe question de dépassement de pile et les réponses ici , et j'ai écrit un beaucoup plus verbeux article de blog qui résume les diverses considérations. Les parties de cette solution qui sont pertinentes ici sont, dans le contrôleur d’application:
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;
}
};
}
);
Ça marche côté angularjs!