11

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

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

4

2 回答 2

9

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

require 'rack/session/abstract/id'

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

文件:

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

所有参数都是可选的。

  • :key 确定 cookie 的名称,默认为 'rack.session'
  • :path、:domain、:expire_after、:secure 和 :httponly 设置相关的 cookie 选项,如 Rack::Response#add_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
于 2013-03-19T13:50:46.723 回答
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和类中看到 cookie 交互有点奇怪#set_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地方都是一样的。

于 2015-02-17T11:22:22.973 回答