Rails 3禁用会话cookie

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

我试过使用session :offreset_session但它没有任何意义。 我使用devise作为身份validation框架。

这是我的ApplicationController

 class ApplicationController  :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 

正如John对答案的评论中所提到的,清除会话不会阻止会话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 

为了防止cookiefilter最终出现在应用程序堆栈跟踪中,有时可能会让人感到困惑,您可能希望在回溯中将其静音(假设您将其放在lib/cookie_filter.rb ):

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

使用内置选项

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

或等同物

 request.session_options[:skip] = true 

你可以在这里找到它的文档http://doc.rubyists.com/rack/Rack/Session/Abstract/ID.html

我不确定他们何时将它添加到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发出初始请求以生成/检索令牌。 即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 

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

另一种解决方案:在控制器中你想避免使用cookies,添加:

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

如果你有一组api控制器,在api_controller类中设置它,让你的其他控制器inheritanceapi_controller。

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

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

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标头

继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 )。

我自己真的错过了能够声明性地关闭会话(使用session :off

…因此我把它“带回” – 就像在普通旧轨道(<= 2.2)中一样使用它:

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

https://github.com/kares/session_off

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

为此,请将其添加到application.rb(如果需要,还添加到特定环境):

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

试试这个

 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