【问题标题】:Rails 3 disabling session cookiesRails 3 禁用会话 cookie
【发布时间】:2011-07-23 01:43:47
【问题描述】:

我在 RoR 3 上编写了 RESTful API。 我必须让我的应用程序不发送“Set-Cookie 标头”(客户端正在使用 auth_token 参数进行授权)。

我曾尝试使用session :offreset_session,但这没有任何意义。 我使用devise 作为身份验证框架。

这是我的应用程序控制器

class ApplicationController < ActionController::Base
  before_filter :reset_session #, :unless => :session_required?
  session :off #, :unless => :session_required?

  skip_before_filter :verify_authenticity_token
  before_filter :access_control_headers!

  def options
    render :text => ""
  end

  private
  def access_control_headers!
    response.headers["Access-Control-Allow-Origin"] = "*"
    response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
    response.headers["Access-Control-Allow-Credentials"] = "true"
    response.headers["Access-Control-Allow-Headers"] = "Content-type"
  end

  def session_required?
    !(params[:format] == 'xml' or params[:format] == 'json')
  end
end

【问题讨论】:

  • 我同样想阻止会话创建,但将我的会话存储在数据库中,这样抑制 cookie 就不会减少它。我的解决方案是告诉 Rack 放弃会话:stackoverflow.com/questions/33318060/…

标签: ruby-on-rails ruby ruby-on-rails-3 session cookies


【解决方案1】:
# frozen_string_literal: true

module Api
  module Web
    module Base
      class WebApiApplicationController < ApplicationController

        include DeviseTokenAuth::Concerns::SetUserByToken
        include Api::Concerns::ErrorsConcern

        devise_token_auth_group :user, contains: %i[api_web_v1_user]
        respond_to :json
        serialization_scope :current_user

        before_action :METHOD_NAME

        private

        def METHOD_NAME
          request.session_options[:skip] = true
        end

      end
    end
  end
end

它对我有用。

【讨论】:

    【解决方案2】:

    使用内置选项

    env['rack.session.options'][:skip] = true
    

    或同等的

    request.session_options[:skip] = true
    

    你可以在这里找到它的文档https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L213

    【讨论】:

    • 谢谢。这很简单而且非常有效。在我的会话控制器中使用了这个,用于 json 请求的 before_action
    • 真的很有帮助!正是我需要的。有人知道如何用 RSpec 正确测试这个案例吗?
    • 发现:```expect(request.session_options[:skip]).to eq true ``
    • 像魅力一样工作!
    • 找到了为什么它在这里工作的解释:github.com/rack/rack/blob/master/lib/rack/session/abstract/…
    【解决方案3】:

    正如对约翰回答的评论中提到的,清除会话不会阻止会话 cookie 被发送。如果您希望完全删除发送的 cookie,您必须使用 Rack 中间件。

    class CookieFilter
      def initialize(app)
        @app = app
      end
    
      def call(env)
        status, headers, body = @app.call(env)
    
        # use only one of the next two lines
    
        # this will remove ALL cookies from the response
        headers.delete 'Set-Cookie'
        # this will remove just your session cookie
        Rack::Utils.delete_cookie_header!(headers, '_app-name_session')
    
        [status, headers, body]
      end
    end
    

    通过创建具有以下主体的初始化程序来使用它:

    Rails.application.config.middleware.insert_before ::ActionDispatch::Cookies, ::CookieFilter
    

    为了防止 cookie 过滤器最终出现在应用程序堆栈跟踪中,这有时会完全令人困惑,您可能希望在回溯中将其静音(假设您将其放入 lib/cookie_filter.rb):

    Rails.backtrace_cleaner.add_silencer { |line| line.start_with? "lib/cookie_filter.rb" }
    

    【讨论】:

    • 它删除了flash[]。使用这个中间件类,不可能在一个动作中在flash[] 中设置一些值,将redirect_to 设置为另一个动作并在另一个动作中从flash[] 中获取您的值。如果您想在重定向之间保留数据,它也不允许设置自定义 cookie 或会话。此类只是删除所有内容。
    • 我意识到这是一个旧评论,但是将 initializers/session_store.rb 更改为包含:PlayreadyLicense::Application.config.session_store :disabled 以禁用会话 cookie 有什么问题?
    • 这对 Rails4 有效吗?
    • @SaadMasood 请改用request.session_options[:skip] = true。查看达尔文的答案
    【解决方案4】:

    我不确定他们何时将其添加到 Devise,但似乎有一个配置可以让您在使用 auth_token 时禁用会话 cookie 的发送:

    # By default Devise will store the user in session. You can skip storage for
    # :http_auth and :token_auth by adding those symbols to the array below.
    # Notice that if you are skipping storage for all authentication paths, you
    # may want to disable generating routes to Devise's sessions controller by
    # passing :skip => :sessions to `devise_for` in your config/routes.rb
    config.skip_session_storage = [:http_auth, :token_auth]
    

    效果很好。我遇到的唯一问题是我仍然需要能够向我的token_controller 发出初始请求才能生成/检索令牌。 IE。 POST /api/v1/tokens.json,不幸的是,这会导致为该请求返回会话 cookie。

    所以我最终实现了 Ryan Ahearn 上面写的 CookieFilter 初始化器。

    另外,由于我的应用程序同时具有 Web 前端和 JSON api,我只想过滤 JSON api 的 cookie。于是我修改了CookieFilter类,先检查属于该api的请求:

    if env['PATH_INFO'].match(/^\/api/)
      Rack::Utils.delete_cookie_header!(headers, '_myapp_session')
    end
    

    不确定是否有更好的方法...

    【讨论】:

    • 如何在没有会话的情况下使用 signed_incurrent_user 这样的设计助手?
    【解决方案5】:

    另一种解决方案: 在要避免使用 cookie 的控制器中,添加以下内容:

    after_filter :skip_set_cookies_header
    
    def skip_set_cookies_header
      request.session_options = {}
    end
    

    如果您有一组 api 控制器,请将其设置在 api_controller 类中,并让您的其他控制器继承 api_controller。

    这会跳过设置 Set-Cookie 标头,因为会话 opts 为空。

    【讨论】:

    • 这在我的 Rails 4 应用程序上运行良好,但在升级到 Rails 5 后导致(很难找到)运行时错误。对于开始看到“{} 的未定义方法 ID”错误的任何人来说,请注意在他们的堆栈跟踪中。
    【解决方案6】:

    Imo 最好的方法是简单地删除 cookie 会话存储中间件。

    为此,请将其添加到您的 application.rb(或根据需要添加到特定环境):

    # No session store
    config.middleware.delete ActionDispatch::Session::CookieStore
    

    【讨论】:

      【解决方案7】:

      我自己真的很怀念能够以声明方式关闭会话(使用session :off

      ...因此我将它“带回” - 就像在普通旧导轨(

      当然,这可能需要您自己进行一些额外的 Devise 特定黑客攻击,因为 session_off 可能会在控制器中导致 session == nil,并且自 2.3 以来的大多数 Rails 扩展只是假设一个永远不会为零的惰性会话。

      https://github.com/kares/session_off

      【讨论】:

        【解决方案8】:

        试试这个

        after_filter :skip_set_cookies_header
        
        def skip_set_cookies_header
          session.instance_variable_set('@loaded', false)
        end
        

        或者更好的是,当会话数据没有改变时,总是删除 Set-Cookie 标头

        before_filter :session_as_comparable_array # first before_filter
        after_filter :skip_set_cookies_header      # last  after_filter
        
        def session_as_comparable_array(obj = session)
          @session_as_comparable_array = case obj
          when Hash
            obj.keys.sort_by(&:to_s).collect{ |k| [k, session_as_comparable_array(obj[k])] }
          when Array
            obj.sort_by(&:to_s).collect{ |k| session_as_comparable_array(k) }
          else
            obj
          end
        end
        
        def skip_set_cookies_header
          session.instance_variable_set('@loaded', false) if (@session_as_comparable_array == session_as_comparable_array)
        end
        

        【讨论】:

        • 我认为不言而喻,像这样深入研究对象的实例变量是一种非常肮脏的黑客行为,当底层实现细节在未来的更新中发生变化时,你会要求它中断: -P
        【解决方案9】:

        对于 John 的回答,如果您使用 CSRF 保护,则需要针对 Web 服务请求将其关闭。您可以在应用程序控制器中添加以下内容作为受保护的方法:

          def protect_against_forgery?
            unless request.format.xml? or request.format.json?
              super
            end
          end
        

        这种方式的 HTML 请求仍然使用 CSRF(或不使用 - 取决于环境中的 config.action_controller.allow_forgery_protection = true/false)。

        【讨论】:

        • 我发现只需从我的布局中删除 &lt;%= csrf_meta_tags %&gt; 就可以了。
        • @JoLiss 这意味着您正在损害 CSRF 保护——这不是解决问题的合理方法。元标记用于验证修改数据的 AJAX 请求(非GET),因此您可能会通过忽略这些类型的请求来获得异常。尽管如此,Sayantam 试图说明的内容不应该是必要的,因为 Rails 只验证 HTML 和 JS(而不是 JSON)请求——请参阅:api.rubyonrails.org/classes/ActionController/…weblog.rubyonrails.org/2011/2/8/…
        • 好的,感谢您指出这一点。我是为不需要 CSRF 保护的站点执行此操作的——通常,删除 csrf_meta_tags 实际上不是一个好主意。
        【解决方案10】:

        默认的 CookieSessionStore 不会发送“Set-Cookie”标头,除非向会话中添加了某些内容。您的堆栈中是否有东西写入会话? (可能是设计)

        session :off 已被弃用:

        def session(*args)
          ActiveSupport::Deprecation.warn(
            "Disabling sessions for a single controller has been deprecated. " +
            "Sessions are now lazy loaded. So if you don't access them, " +
            "consider them off. You can still modify the session cookie " +
            "options with request.session_options.", caller)
        end
        

        如果您的堆栈中的某些内容正在设置会话信息,您可以使用 session.clear 将其清除,如下所示:

        after_filter :clear_session
        
        def clear_session
          session.clear
        end
        

        这将阻止发送 Set-Cookie 标头

        【讨论】:

        • session.clear 删除整个用户的会话。它不会阻止发送 Set-Cookie 标头;相反,它发出一个新的 Set-Cookie,完全清空用户的会话。
        • @john “CookieSessionStore 不会发送“Set-Cookie”标头,除非将某些内容添加到会话中”我也这么认为!但是 Set-Cookie 现在设置在机架级别 action_dispatch/middleware/cookies.rb 是 duh
        猜你喜欢
        • 2011-04-29
        • 2013-04-28
        • 2011-04-11
        • 2011-05-11
        • 2011-05-22
        • 1970-01-01
        • 2012-12-02
        • 2018-10-10
        • 1970-01-01
        相关资源
        最近更新 更多