我有一个使用 Devise 进行身份验证的 Rails 7 SaSS 应用程序,该应用程序在
www.my-site.com
子域上注册新用户,并在 dashboard.my-site.com
注册后将其重定向到实际应用程序。
我可以使用以下 session_store.rb 代码轻松完成此任务:
Rails.application.config.session_store :cookie_store, key: "_myapp_session", domain: ["dashboard.my-site.com", "www.my-site.com"], tld_length: 2
但问题是我的应用程序允许用户拥有自定义子域 - 应该有完全独立的 cookie - 例如,
a-customer-subdomain.my-site.com
所以我的想法是,我实际上需要提前动态设置 :cookie_store - 就像在中间件中一样 - 因此我们对
www
和 dashboard
子域使用一种设置,对任何其他子域或域使用完全不同的设置。但这就是我发现自己陷入困境的地方。我该如何进行这些调整?
看看“Rails 3
session_store domain :all
真正做了什么?”,还有Prathamesh Sonpatki的“使用多个子域构建Rails应用程序”(2020年3月),您确实应该使用中间件来动态设置基于传入请求的 cookie 域。
# lib/middleware/dynamic_cookie_domain.rb
class DynamicCookieDomain
def initialize(app)
@app = app
end
def call(env)
host = env["HTTP_HOST"].split(':').first
Rails.application.config.session_store :cookie_store, key: "_myapp_session", domain: custom_domain(host), tld_length: 2
@app.call(env)
end
private
def custom_domain(host)
case host
when 'www.my-site.com', 'dashboard.my-site.com'
['dashboard.my-site.com', 'www.my-site.com']
else
host
end
end
end
如果请求来自这些主机中的任何一个,则此中间件会将会话 cookie 域设置为
['dashboard.my-site.com', 'www.my-site.com']
。对于任何其他主机,它将把会话 cookie 域设置为请求的主机。
您需要将此中间件添加到您的中间件堆栈中。您可以在应用程序配置中执行此操作:
# config/application.rb
require_relative '../lib/middleware/dynamic_cookie_domain'
module YourApp
class Application < Rails::Application
# ...
config.middleware.use DynamicCookieDomain
# ...
end
end
这会将
DynamicCookieDomain
中间件插入到您的中间件堆栈中,确保它针对每个请求运行。
这应该满足
www
和 dashboard
子域与任何其他子域具有不同 cookie 设置的要求。
中间件中的
custom_domain
方法检查传入请求的主机。
如果主机是
www.my-site.com
或 dashboard.my-site.com
,它将 cookie 域设置为 ['dashboard.my-site.com', 'www.my-site.com']
。这意味着会话 cookie 将在这两个子域之间共享。
对于任何其他主机(可能是任何其他子域或域),它将 cookie 域设置为请求的主机。这意味着会话 cookie 将特定于该子域或域,并且不会与
www.my-site.com
或 dashboard.my-site.com
共享。
这种基于传入请求的 Cookie 域动态设置应该允许您对
www
和 dashboard
子域进行一种设置,并对任何其他子域或域进行不同的设置。
这与 this answer 略有不同,如果请求来自子域,则将 cookie 域设置为二级域(例如
my-site.com
),否则设置为 :all
。这将在 my-site.com
的所有子域之间共享 cookie。
相比之下,如果请求来自这些主机中的任何一个,则 cookie 域设置为
['dashboard.my-site.com', 'www.my-site.com']
,否则设置为请求的主机。这会在 dashboard.my-site.com
和 www.my-site.com
之间共享 Cookie,但对所有其他子域使用单独的 Cookie。
我一直无法弄清楚如何动态更改 session_storage 键
尝试动态更改中间件内的会话存储配置似乎无效,可能是因为会话存储配置是在 Rails 应用程序启动时初始化的,而中间件内的后续修改没有产生预期的影响。
ActionDispatch::Request
对象的会话选项,而不是尝试重新配置会话存储。
尝试修改
DynamicCookieDomain
中间件来更改 ActionDispatch::Request
对象的会话选项:
# lib/middleware/dynamic_cookie_domain.rb
class DynamicCookieDomain
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
host = request.host
domain = custom_domain(host)
request.session_options[:domain] = domain
@app.call(request.env)
end
private
def custom_domain(host)
case host
when 'www.my-site.com', 'dashboard.my-site.com'
'.my-site.com'
else
host
end
end
end
确保中间件已加载并在您的应用程序中使用:
# config/application.rb
require_relative '../lib/middleware/dynamic_cookie_domain'
module YourApp
class Application < Rails::Application
# ...
config.middleware.use DynamicCookieDomain
# ...
end
end
使用
DynamicCookieDomain
中间件,call
方法从 ActionDispatch::Request
哈希创建 env
对象,然后修改 session_options
对象的 Request
以设置所需的域。然后,中间件使用 env
对象中更新的 Request
哈希值调用中间件堆栈中的下一项。
这应该根据每个请求更改会话选项,而不是全局重新配置会话存储。