【问题标题】:Is there a Rack middleware for using sessions without cookies?是否有使用没有 cookie 的会话的 Rack 中间件?
【发布时间】:2013-03-19 12:51:43
【问题描述】:

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

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

【问题讨论】:

    标签: ruby session cookies rack


    【解决方案1】:

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

    require 'rack/session/abstract/id'
    

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

    文档:

    ID 为实现基于 id 的会话建立了一个基本框架 服务。发送给客户端用于维护会话的 Cookie 只会 包含一个 id 参考。只有 #get_session 和 #set_session 是 需要被覆盖。

    所有参数都是可选的。

    • :key决定cookie的名字,默认是 '机架.会话'
    • :path, :domain, :expire_after, :secure, :httponly 设置相关 Rack::Response#add_cookie 的 cookie 选项
    • :skip 不会在响应中设置 cookie,也不会更新会话状态
    • :defer 不会在响应中设置 cookie 但仍会更新会话 说明它是否与后端一起使用
    • :renew(依赖于实现)会提示生成一个新的 会话 id,以及要在新 id 处引用的数据的迁移。如果 :defer 已设置,它将被覆盖并设置 cookie。
    • :sidbits 设置生成会话的长度位数 id 会。

    这些选项可以在每个请求的基础上设置,在 env['rack.session.options']。此外,会话的 id 可以是 在键 :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
    

    【讨论】:

    • 谢谢 - 看起来不错。通过浏览代码,我相信我需要设置 :defer 而不是 :skip 参数。我做完一些实验后会回帖。
    • 在这个例子中,session[:user_id] 在哪里被设置为一个非 nil 的实际值?那么,客户端如何将 session_id 传回服务器,以取回上一次请求时 session[:user_id] 设置的值?
    【解决方案2】:

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

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

    在方法#commit_session#set_cookie 中看到Rack::Session::Abstract::ID 类中的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 在任何地方都一样。

    【讨论】:

    • ActionDispatch::Session::CacheStore 会起作用吗?这不是会话的重点(而不是身份验证令牌):使用与数据库不同的存储吗?
    • @Vanuan,我不喜欢将缓存与会话混合的想法。在实现中它们可能非常相似,但在某些时候缓存可能会推出您的会话,这将是这种方法的可怕副作用。
    • @Vanuan,在我的理解中,会话和身份验证令牌是两个不同的东西。会话代表属于特定用户的某些状态,身份验证令牌是一个唯一序列,可让您访问该状态。使用加密/签名,您可以将身份验证令牌/cookie 在会话存储的同时进行组合。几乎总是在每次请求时,您都必须恢复会话并从中获取一些信息。因此,尽可能快地使用它是有意义的,并且在内存中使用键/值存储只是解决此问题的一种方法。
    • 没有什么能阻止你使用数据库,它只会在速度和系统吞吐量方面效率低下。
    • 我的意思是,如果你使用 session-id 作为你的“令牌”,如果它存储在内存中,你将无法列出所有使用的令牌,而如果它存储在一个数据库。 IE。存储在内存中更安全。
    猜你喜欢
    • 2013-08-22
    • 2016-07-02
    • 2011-04-10
    • 2011-04-14
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多