Ruby-on-rails Rails CSRF 保护 + Angular.js:protect_from_forgery 让我在 POST 上注销
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14734243/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST
提问by Paul
If the protect_from_forgeryoption is mentioned in application_controller, then I can log in and perform any GET requests, but on very first POST request Rails resets the session, which logs me out.
如果protect_from_forgeryapplication_controller 中提到了该选项,那么我可以登录并执行任何 GET 请求,但是在第一个 POST 请求时,Rails 会重置会话,从而将我注销。
I turned the protect_from_forgeryoption off temporarily, but would like to use it with Angular.js. Is there some way to do that?
我protect_from_forgery暂时关闭了该选项,但想在 Angular.js 中使用它。有没有办法做到这一点?
回答by HungYuHei
I think reading CSRF-value from DOM is not a good solution, it's just a workaround.
我认为从 DOM 读取 CSRF 值不是一个好的解决方案,它只是一种解决方法。
Here is a document form angularJS official website http://docs.angularjs.org/api/ng.$http:
这是angularJS官网http://docs.angularjs.org/api/ng.$http的文档形式 :
Since only JavaScript that runs on your domain could read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.
To take advantage of this (CSRF Protection), your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on first HTTP GET request. On subsequent non-GET requests the server can verify that the cookie matches X-XSRF-TOKEN HTTP header
由于只有在您的域上运行的 JavaScript 才能读取 cookie,因此您的服务器可以确信 XHR 来自在您的域上运行的 JavaScript。
为了利用这一点(CSRF 保护),您的服务器需要在第一个 HTTP GET 请求时在名为 XSRF-TOKEN 的 JavaScript 可读会话 cookie 中设置一个令牌。在后续的非 GET 请求中,服务器可以验证 cookie 是否与 X-XSRF-TOKEN HTTP 标头匹配
Here is my solution based on those instructions:
这是我基于这些说明的解决方案:
First, set the cookie:
首先,设置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
Then, we should verify the token on every non-GET request.
Since Rails has already built with the similar method, we can just simply override it to append our logic:
然后,我们应该在每个非 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
回答by Michelle Tilley
If you're using the default Rails CSRF protection (<%= csrf_meta_tags %>), you can configure your Angular module like this:
如果您使用默认的 Rails CSRF 保护 ( <%= csrf_meta_tags %>),您可以像这样配置您的 Angular 模块:
myAngularApp.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
]
Or, if you're not using CoffeeScript (what!?):
或者,如果您没有使用 CoffeeScript(什么!?):
myAngularApp.config([
"$httpProvider", function($httpProvider) {
$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}
]);
If you prefer, you can send the header only on non-GET requests with something like the following:
如果您愿意,您可以仅在非 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
]
Also, be sure to check out HungYuHei's answer, which covers all the bases on the server rather than the client.
另外,一定要查看HungYuHei 的答案,它涵盖了服务器上的所有基础,而不是客户端。
回答by jsanders
The angular_rails_csrfgem automatically adds support for the pattern described in HungYuHei's answerto all your controllers:
该angular_rails_csrf宝石会自动为所描述的模式支持HungYuHei的回答您的所有控制器:
# Gemfile
gem 'angular_rails_csrf'
回答by Anton Orel
The answer that merges all previous answers and it relies that you are using Deviseauthentication gem.
合并所有先前答案的答案,它依赖于您正在使用Devise身份验证 gem。
First of all, add the gem:
首先,添加宝石:
gem 'angular_rails_csrf'
Next, add rescue_fromblock into application_controller.rb:
接下来,将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
And the finally, add the interceptor module to you angular app.
最后,将拦截器模块添加到您的 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')
回答by Ruby Racer
I found a very quick hack to this. All I had to do is the following:
我找到了一个非常快速的破解方法。我所要做的就是以下几点:
a. In my view, I initialize a $scopevariable which contains the token, let's say before the form, or even better at controller initialization:
一种。在我看来,我初始化了一个$scope包含令牌的变量,比方说在表单之前,或者在控制器初始化时更好:
<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">
b. In my AngularJS controller, before saving my new entry, I add the token to the hash:
湾 在我的 AngularJS 控制器中,在保存我的新条目之前,我将令牌添加到哈希中:
$scope.addEntry = ->
$scope.newEntry.authenticity_token = $scope.authenticity_token
entry = Entry.save($scope.newEntry)
$scope.entries.push(entry)
$scope.newEntry = {}
Nothing more needs to be done.
没有什么需要做的了。
回答by PaulL
I've used the content from HungYuHei's answer in my application. I found that I was dealing with a few additional issues however, some because of my use of Devise for authentication, and some because of the default that I got with my application:
我在我的申请中使用了 HungYuHei 的回答中的内容。然而,我发现我正在处理一些额外的问题,有些是因为我使用 Devise 进行身份验证,有些是因为我的应用程序默认:
protect_from_forgery with: :exception
I note the related stack overflow question and the answers there, and I wrote a much more verbose blog postthat summarises the various considerations. The portions of that solution that are relevant here are, in the application controller:
我注意到相关的堆栈溢出问题和那里的答案,我写了一篇更详细的博客文章,总结了各种注意事项。该解决方案的相关部分在应用程序控制器中:
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
回答by Blaine Hatab
I saw the other answers and thought they were great and well thought out. I got my rails app working though with what I thought was a simpler solution so I thought I'd share. My rails app came with this defaulted in it,
我看到了其他答案,并认为它们很棒而且经过深思熟虑。我让我的 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
I read the comments and it seemed like that is what I want to use angular and avoid the csrf error. I changed it to this,
我阅读了评论,似乎这就是我想要使用 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
And now it works! I don't see any reason why this shouldn't work, but I'd love to hear some insight from other posters.
现在它起作用了!我看不出有任何理由为什么这不可行,但我很想听听其他海报的一些见解。
回答by Evgeniy Krokhmal
angular
.module('corsInterceptor', ['ngCookies'])
.factory(
'corsInterceptor',
function ($cookies) {
return {
request: function(config) {
config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN');
return config;
}
};
}
);
It's working on angularjs side!
它正在 angularjs 方面工作!

