Ruby on rails 在Rails中的子域之间共享会话(cookies)?

Ruby on rails 在Rails中的子域之间共享会话(cookies)?,ruby-on-rails,session,devise,Ruby On Rails,Session,Devise,我有一个应用程序设置,每个用户都属于一家公司,该公司有一个子域(我使用的是basecamp风格的子域)。我所面临的问题是rails正在创建多个cookie(一个用于lvh.me,另一个用于subdomain.lvh.me),这在我的应用程序中造成了相当多的中断(例如,flash消息在登录后会持久化,尽管所有请求都会断开) 在我的/cofig/initilizers/session\u store.rb文件中有: AppName::Application.config.session_store

我有一个应用程序设置,每个用户都属于一家公司,该公司有一个子域(我使用的是basecamp风格的子域)。我所面临的问题是rails正在创建多个cookie(一个用于lvh.me,另一个用于subdomain.lvh.me),这在我的应用程序中造成了相当多的中断(例如,flash消息在登录后会持久化,尽管所有请求都会断开)

在我的/cofig/initilizers/session\u store.rb文件中有:

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: :all

域名::all似乎是我在谷歌上找到的标准答案,但这似乎对我不起作用。感谢您的帮助

事实证明,“domain:all”为会话期间访问的所有不同子域创建一个cookie(并确保它们在请求之间传递)。如果未传递任何域参数,则意味着将为同一会话中访问的每个不同域创建一个新cookie,而旧cookie将被丢弃。我需要的是一个在整个会话过程中保持不变的cookie,即使域发生了变化。因此,通过
域:“lvh.me”
解决了开发中的问题。这会在不同的子域之间创建一个cookie

对于任何需要进一步解释的人,这是一个很好的链接: 你试过了吗

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: 'lvh.me'  
)


基本上,我们说的是基本域只有一个cookie,而忽略子域。尽管这种方法仍然有一些缺陷

出于某种原因,用域替换
:所有
对我来说都不起作用(rails 3.2.11)。需要一个定制中间件来修复它。该解决方案的摘要如下

tl;dr:您需要编写一个自定义机架中间件。您需要将其添加到您的
conifg/environments/[production | development].rb
中。这是在Rails 3.2.11上

Cookie会话通常仅为顶级域存储

如果您查看
Chrome->Settings->Show advanced Settings…->隐私/内容设置…->所有cookie和站点数据…->搜索{yourdomain.com}
,您可以看到
sub1.yourdomain.com
othersub.yourdomain.com
将有单独的条目

挑战在于跨所有子域使用相同的会话存储文件

步骤1:添加自定义中间件类 这就是我们的优势所在。一些相关机架和导轨资源:

  • 和的机架文档
下面是一个自定义类,您应该将其添加到
lib
这是他写的,你们都应该感谢他

# Custom Domain Cookie
#
# Set the cookie domain to the custom domain if it's present
class CustomDomainCookie
  def initialize(app, default_domain)
    @app = app
    @default_domain = default_domain
  end

  def call(env)
    host = env["HTTP_HOST"].split(':').first
    env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{@default_domain}"
    @app.call(env)
  end

  def custom_domain?(host)
    host !~ /#{@default_domain.sub(/^\./, '')}/i
  end
end
基本上,它会将所有cookie会话数据映射回与根域相同的cookie文件

步骤2:添加到Rails配置 现在您在lib中有了一个自定义类,请确保您正在自动加载它。如果这对你来说毫无意义,请看这里:

第一件事是确保您在系统范围内使用cookie存储。在
config/application.rb
中,我们告诉Rails使用cookie存储

# We use a cookie_store for session data
config.session_store :cookie_store,
                     :key => '_yourappsession',
                     :domain => :all
这里之所以提到这一点,是因为
:domain=>:all
行。还有其他人建议指定
:domain=>”.yourdomain.com“
,而不是
:domain=>:all
。出于某种原因,这对我不起作用,我需要如上所述的自定义中间件类

然后在您的
配置/environments/production.rb中添加:

config.middleware.use "CustomDomainCookie", ".yourdomain.com"
config.middleware.use "CustomDomainCookie", ".lvh.me"
请注意,前面的点是必要的。请参阅“”了解原因

然后在您的
配置/environments/development.rb中添加:

config.middleware.use "CustomDomainCookie", ".yourdomain.com"
config.middleware.use "CustomDomainCookie", ".lvh.me"
lvh.me技巧映射到localhost。太棒了。有关更多信息,请参阅和

希望这样就可以了。老实说,我不完全确定为什么这个过程如此复杂,因为我觉得跨子域站点很常见。如果有人对这些步骤背后的原因有任何进一步的见解,请在评论中告诉我们。

“您需要注意的是,如果您设置:domain=> 当前位置在某些地方建议使用all like,除非 您使用的是localhost.:all默认为TLD长度1,即 这意味着如果您使用Pow(myapp.dev)进行测试,它也不会工作 因为这是长度为2的TLD。”

换句话说,您需要:

 App.config.session_store ... , :domain => :all, :tld_length => 2

清除cookie也是一个好主意

我在寻找将cookie设置为根域的最简单方法时遇到了这个问题。当作为域选项传递时,
:all
选项似乎有一些错误信息。对于大多数域,它实际上会按预期工作,将cookie设置为根域(例如
test.example.com
,对于
test.example.com
)。我认为大多数人在使用域
lvh.me
进行测试时都会遇到问题。rails用于查找顶级域的正则表达式被定义为
domain\u REGEXP=/[^.]*\.([^.]*.\\.\\.\\.\\\.\\.\\\.\\.\\\.$/
。如果注意最后一部分,您可以看到rails将
lvh.me
解释为类似于
com.au
的TLD。如果您的用例需要
lvh.me
才能工作,那么
:all
选项将无法正常工作,然而,对于大多数域来说,它似乎是最简单和最好的选项


TL;DR,这里的正确答案是,假设您不是在一个3个字母的域(或任何混淆上述正则表达式的域)上开发,那么使用
:all

我正在寻找一种方法来解决这个问题,而不必显式声明域名,这样我就可以在localhost、lvh.me和,以及我将在生产中使用的任何域,而无需继续编辑session_store.rb文件。然而,设置“domain::all”似乎并不适合我

最终我发现我需要声明tld_长度(顶级域长度)
Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: :all, tld_length: 2
Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: {
  production: '.example.com',
  development: '.example.dev'
}.fetch(Rails.env.to_sym, :all)
if Rails.env.development?
    Rails.application.config.session_store :redis_store, {
       servers: [
        { host: 'localhost', port: 6379},
      ],
      key: '_app_session',
      expire_after: 1.day,
      domain: :all
    }

else
    Rails.application.config.session_store :redis_store, {
       servers: [
        { host: HOST_URL, port: PORT},
      ],
      key: '_app_session',
      expire_after: 1.day,
      domain: '.domain.com',
      tld_length: 2
    }
    
end