130

如果protect_from_forgery在 application_controller 中提到了该选项,那么我可以登录并执行任何 GET 请求,但在第一个 POST 请求时,Rails 会重置会话,这会将我注销。

protect_from_forgery暂时关闭了该选项,但想将它与 Angular.js 一起使用。有没有办法做到这一点?

4

8 回答 8

277

我认为从 DOM 读取 CSRF-value 不是一个好的解决方案,它只是一种解决方法。

这里是 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
于 2013-04-02T10:30:34.320 回答
79

如果你使用默认的 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 的答案,它涵盖了服务器而不是客户端的所有基础。

于 2013-02-06T17:30:36.990 回答
29

angular_rails_csrf gem 自动添加对HungYuHei对所有控制器的回答中描述的模式的支持:

# Gemfile
gem 'angular_rails_csrf'
于 2013-12-13T17:18:19.670 回答
4

合并所有先前答案的答案,它依赖于您正在使用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')
于 2014-05-28T15:53:38.063 回答
1

我看到了其他答案,并认为它们很棒并且经过深思熟虑。我让我的 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

现在它起作用了!我看不出这不应该起作用的任何原因,但我很想听听其他海报的一些见解。

于 2013-09-09T14:39:34.980 回答
1

我在我的应用程序中使用了 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
于 2014-04-17T14:06:52.587 回答
1

我发现了一个非常快速的破解方法。我所要做的就是:

一种。在我看来,我初始化了一个$scope包含令牌的变量,比如说在表单之前,或者在控制器初始化时更好:

<div ng-controller="MyCtrl" ng-init="authenticity_token = '<%= form_authenticity_token %>'">

湾。在我的 AngularJS 控制器中,在保存我的新条目之前,我将令牌添加到哈希中:

$scope.addEntry = ->
    $scope.newEntry.authenticity_token = $scope.authenticity_token 
    entry = Entry.save($scope.newEntry)
    $scope.entries.push(entry)
    $scope.newEntry = {}

没有什么需要做的了。

于 2014-12-30T22:21:47.463 回答
0
 angular
  .module('corsInterceptor', ['ngCookies'])
  .factory(
    'corsInterceptor',
    function ($cookies) {
      return {
        request: function(config) {
          config.headers["X-XSRF-TOKEN"] = $cookies.get('XSRF-TOKEN');
          return config;
        }
      };
    }
  );

它在angularjs方面工作!

于 2017-02-16T15:04:38.700 回答