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 :off
和reset_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_in
或 current_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
)。
【讨论】:
我发现只需从我的布局中删除<%= csrf_meta_tags %>
就可以了。
@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的主要内容,如果未能解决你的问题,请参考以下文章