Rack 提供的会话管理中间件都是基于 cookie 来识别用户的。由于我正在开发一个 api,我宁愿将 session-id 作为查询字符串参数显式传递。查看代码库,似乎没有考虑到这个用例,因为所有会话中间件都从一个通用类扩展而来,该类读取/写入 cookie。
所以我的问题是 - 是否有一个项目可以维护替代机架中间件或机架内置中间件的猴子补丁,这将允许我在查询字符串上维护会话 ID,而不是 cookie 存储?
Rack 可以使用自定义会话 ID 项而不是 cookie:
require 'rack/session/abstract/id'
Rack 文档可能是您开始搜索的一个有用的地方。我相信您正在寻找“跳过”选项(或“延迟”选项)。
文件:
ID 为实现基于 id 的会话服务建立了一个基本框架。发送给客户端以维护会话的 Cookie 将仅包含一个 id 引用。只有#get_session 和#set_session 需要被覆盖。
所有参数都是可选的。
这些选项可以根据每个请求在 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
如果您想在 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
地方都是一样的。