Rails 3 禁用会话 cookie

Posted

技术标签:

【中文标题】Rails 3 禁用会话 cookie【英文标题】:Rails 3 disabling session cookies 【发布时间】: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 放弃会话:***.com/questions/33318060/… 【参考方案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

以上是关于Rails 3 禁用会话 cookie的主要内容,如果未能解决你的问题,请参考以下文章

在 Rails 3 中跨多个子域删除会话 Cookie

如何手动解密 Rails 5 会话 cookie?

Rails - 多个***域和单个会话/cookie

在 Rails 中的子域之间共享会话(cookie)?

可以禁用会话存储/本地存储并启用 Cookies 吗?

给定会话密钥和秘密,我们如何解密 Rails cookie?