是否有Rack中间件用于使用没有cookie的会话?

时间:2013-03-19 12:51:43

标签: ruby session cookies rack

随Rack提供的会话管理中间件都基于用于识别用户的cookie。由于我正在开发api,我宁愿将session-id显式地作为查询字符串参数传递。看一下代码库,似乎并没有考虑这个用例,因为所有的会话中间件都是从一个读写/写入cookie的公共类扩展而来。

所以我的问题是 - 是否有一个项目维护备用Rack中间件或机架内置中间件的猴子补丁,这将允许我在查询字符串而不是cookie存储上维护会话ID?

2 个答案:

答案 0 :(得分:7)

Rack可以使用自定义会话ID项而不是cookie:

require 'rack/session/abstract/id'

Rack文档可能是一个有用的开始搜索的地方。我相信你正在寻找“跳过”选项(或“推迟”选项)。

文档:

ID为实现基于身份的会话设置了一个基本框架 服务。发送给客户端以维护会话的Cookie只会 包含id引用。只有#get_session和#set_session 需要被覆盖。

所有参数都是可选的。

  • :key确定cookie的名称,默认为 'rack.session'
  • :path,:domain,:expire_after,:secure,和:httponly设置相关 通过Rack :: Response#add_cookie
  • 的cookie选项
  • :skip不会在响应中设置cookie也不会更新会话状态
  • :defer不会在响应中设置cookie但仍会更新会话 如果它与后端一起使用则说明
  • :续订(依赖于实现)将提示生成新的 会话ID,以及要在新ID处引用的数据的迁移。如果 :设置延迟,它将被覆盖并且cookie将被设置。
  • :sidbits设置生成的会话的长度位数 id将是。

可以在每个请求的基础上设置这些选项 env中[ 'rack.session.options']。此外,会话的ID可以是 在key:id的选项哈希中找到。这非常不是 建议改变它的价值。

Rack :: Utils :: Context兼容。

默认不包括在内;你必须要求'rack / session / abstract / id'才能使用。

来源:

  class ID
    DEFAULT_OPTIONS = {
      :key =>           'rack.session',
      :path =>          '/',
      :domain =>        nil,
      :expire_after =>  nil,
      :secure =>        false,
      :httponly =>      true,
      :defer =>         false,
      :renew =>         false,
      :sidbits =>       128,
      :cookie_only =>   true,
      :secure_random => (::SecureRandom rescue false)
    }

我希望这能带给你领导......当你了解更多信息时,你能在这里分享你的成果吗?

编辑:

神奇的诀窍是将选项:cookie_only => false:defer => true结合起来。当然,标准的Rack :: Session :: Cookie在这里没有多大意义,所以你可以这样做:

use Rack::Session::Pool, :cookie_only => false, :defer => true

有趣的是,您可以在运行时更改选项。在我的用例中,我实际上需要支持传统的基于cookie的机制以及显式参数传递样式,所以我做了以下内容:

class WebApp < Sinatra::Base

  configure do
    use Rack::Session::Pool, :key => 'session_id'
  end

  before do
    # Switch to parameter based session management if the client is an ios device
    if env['HTTP_USER_AGENT'] =~ /iOS/
      session.options[:cookie_only] = false
      session.options[:defer] = true
    end
  end

  get '/' do
    session[:user_id] ||= nil # This triggers a session-write, giving us a valid session-id
    body "session_id=#{session.id}"
  end
end

答案 1 :(得分:3)

如果您想在API应用程序中消除cookie的使用,但仍想管理会话。例如,在我的情况下,会话标识符来自令牌。 您需要重新定义方法extract_session_id以从接收的令牌中提取会话标识符。必须在会话存储类上重新定义此方法,因为Rack::Session::Abstract::ID基于cookie提供默认实现。 它是从Rack::Session::Abstract::ID#current_session_id方法调用的,它在会话对象上调用#id方法,通常由Rack::Session::Abstract::SessionHash实例表示。在最后调用会话存储的SessionHash#id方法#extract_session_id中。

我认为将会话标识符提取器独立配置会更简单,但是希望将会话标识符与会话数据一起存储在cookie中导致该欺骗设计。

同样在方法Rack::Session::Abstract::ID#commit_session中查看#set_cookie类中的Cookie互动有点奇怪。 因此,要完全确定不会设置cookie,您也可以在商店中重新定义#set_cookie方法。可以通过为会话存储中间件设置cookie_only: false, defer: true来实现相同的目标,如此答案中所述,但我没有检查过。

当然,您需要修改中间件堆栈以排除与Cookie交互的所有中间件以及可能特定于浏览器的中间件。在默认的rails中间件堆栈上,它可以如下所示:

# config/application.rb
[
  Rack::MethodOverride, # browser specific
  ActionDispatch::Cookies,
  ActionDispatch::Flash
].each do |middleware|
  config.middleware.delete(middleware)
end

同样,你肯定需要将会话存储替换为在服务器端存储信息的东西,例如redis,例如:

# config/initializers/session_store.rb
Rails.application.config.session_store ::Custom::Session::TokenRedisStore

就我而言,::Custom::Session::TokenRedisStore继承::RedisSessionStore并重新定义了上述所有方法。 ::RedisSessionStore宝石中包含的redis-session-store。因此,如果您要使用它,显然需要将其添加到Gemfile

我是为Rails 4.2.x做的,但是同样的方法可以在任何框架中被采用,Rack到处都是。