在Rails的子域之间共享会话(cookie)?


92

我有一个应用程序设置,其中每个用户都属于一家公司,并且该公司有一个子域(我正在使用basecamp样式的子域)。我面临的问题是,rails正在创建多个cookie(一个用于lvh.me,另一个用于subdomain.lvh.me),这导致我的应用程序中出现了很多中断(例如,虽然所有请求都一次通过,但Flash消息仍然存在)登录)。

我在/cofig/initilizers/session_store.rb文件中有此文件:

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

域::all似乎是我在Google上找到的标准答案,但这似乎对我不起作用。任何帮助表示赞赏!

Answers:


74

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

对于需要进一步说明的人,这是一个很好的链接:http : //excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/


2
谢谢老兄..我已经面临这个问题上我的项目之一。最后找到了解决办法..
Shirjeel阿拉姆

3
请确保config.secret_key_base在所有应用程序中使用相同的内容,否则它将无法解码Cookie。
Bruno Buccolo 2014年

5
我没有看到与Rails 4相关的任何问题。您知道这是否已更改。我无法使其与我的项目一起工作。它一直在重新创建cookie。谢谢。
安迪

如果我想用来CacheStore将会话存储在memcached中怎么办?
阿米特·帕特尔

2
使用Rails4,我发现这仅适用于带有破折号但不带有下划线的子域:Appname::Application.config.session_store :cookie_store, key: '_appname_session', domain: :all, tld_length: 2
user1515295

68

http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

“在这里要注意的部分是,如果在某些地方建议设置:domain =>:all like,除非您使用本地主机,否则它根本无法工作。:all默认为1的TLD长度,这意味着如果您正在使用Pow(myapp.dev)进行测试,则该方法也不起作用,因为这是长度为2的TLD。”

换句话说,您需要:

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

清除Cookie也是个好主意


1
这是最好的答案,因为一项更改适用于所有环境(app.com和app.dev)。自定义中间件不是必需的。清除Cookie的好方法!
图拉格2013年

1
您错过了, :tld_length => 2
montrealmike 2014年

1
请确保config.secret_key_base在所有应用程序中使用相同的内容,否则它将无法解码Cookie。
Bruno Buccolo 2014年

4
:domain => :all请尝试在Rails 4中使用domain => 'lvh.me', tld_length = 2。它为我工作
Minh Triet

1
使用Rails 4.2,我domain: :all, tld_length: 2在使用lvh.me域时获得了很好的结果。
zwippie 2015年

24

我正在寻找一种无需明确声明域名即可解决此问题的方法,因此我可以在localhost,lvh.me和生产中使用的任何域之间切换,而不必继续编辑session_store.rb文件。但是,设置“ domain :: all”似乎不适用于我。

最终,我发现我需要在该表达式中声明tld_length(顶级域长度)。例如,默认的tld_length为1,而example.lvh.me的tld_length为2,而127.0.0.1.xip.io的tld_length为5。因此,我在session_store.rb文件中在lvh.me上开发中的子域中的内容以及生产中的其他内容如下。

MyApp::Application.config.session_store :cookie_store, key: '_MyApp_session', domain: :all, tld_length: 2

希望这对某人有帮助,因为我花了很长时间才找到此答案!


19

由于某种原因:all,对我来说,替换该域不起作用(第3.2.11条)。它花费了一部分自定义中间件来解决。该解决方案的摘要如下。

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

Cookie会话通常仅存储在您的顶级域中。

如果您查看,Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com}您将看到sub1.yourdomain.comothersub.yourdomain.com和将有单独的条目yourdomain.com

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

步骤1:添加自定义中间件类

这是机架中间件出现的地方。一些相关的机架和导轨资源:

这是一个自定义类,您应该添加到其中。lib 这是@Nader编写的,大家都应该感谢他

# 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中有一个自定义类,请确保正在自动加载它。如果这对您没有任何意义,请看这里:Rails 3自动加载

第一件事是确保您使用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。由于某种原因,这对我不起作用,因此我需要如上所述的自定义中间件类。

然后在您的config/environments/production.rb添加:

config.middleware.use "CustomDomainCookie", ".yourdomain.com"

请注意,前面的点是必需的。有关原因,请参见“ 在父域请求中发送的子域cookie? ”。

然后在您的config/environments/development.rb添加:

config.middleware.use "CustomDomainCookie", ".lvh.me"

lvh.me技巧映射到本地主机。这很棒。有关更多信息,请参见此Railscast有关子域的信息本说明

希望那应该做。老实说,我不确定为什么会如此复杂,因为我觉得跨子域站点很常见。如果有人对这些步骤背后的原因有任何进一步的了解,请在评论中启发我们。


有没有办法使它与多个顶级域一起使用?我有在不同国家/地区运行的产品。在这里,我们假设默认域为yourdomain.com,但是如果该域名适用于.be .sv .fr .com .br .com.ar和其他域名,该怎么办?谢谢。
Marc Lainez 2013年

我只是无法使它正常工作。我正在用Rails 4开发,而rials似乎只是轻轻地忽略了上面的所有代码。它只是不想跨子域共享会话。
Ole HenrikSkogstrøm13年

@OleHenrikSkogstrøm确保config.secret_key_base在所有应用程序中都使用相同的内容,否则它将无法解码cookie。
2014年

17

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

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


谢谢,这终于帮助我理解了为什么这么多的答案建议将tld_length设置为2,但是为什么我却不需要!
汤狗

这个答案需要更高。谢谢先生。
luca.busin

BTW Rails的“ lvh.me是与com.au类似的TLD”,确实应该以同样的方式对.me进行解释,因为它也是国家/地区(黑山)。
mahemoff

7

Rails 4.x(也适用于Rails 5/6版本)

如何在本地主机上获取lvh.me:3000和子域(Rails)

开发:我共享了一些Cookie,可以添加.lvh.me到中session_store.rb

它将在localhost的子域之间共享admin.lvh.me:3000lvh.me:3000依此类推...

#config/initializers/session_store.rb

domain = Rails.env.production? ? ".domain_name.com" : ".lvh.me"

Rails.application.config.session_store :cookie_store, 
                      key: '_app_name_session', domain: domain

4

你试过了吗

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

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


1

支撑轨5

如果您希望它适用于任何域:

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)

参考:https : //github.com/plataformatec/devise/wiki/操作方法:-使用子域


0

如果您将Redis用于会话存储。

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 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.