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

时间:2012-05-01 19:02:40

标签: ruby-on-rails session devise

我有一个应用程序设置,其中每个用户都属于一个公司,并且该公司有一个子域(我使用的是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

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

9 个答案:

答案 0 :(得分:67)

当它变为'domain:all'时,会为该会话期间访问的所有不同子域创建一个cookie(并确保它们在请求之间传递)。如果没有传递域参数,则意味着为在同一会话中访问的每个不同域创建新cookie,旧的cookie被丢弃。我需要的是一个在整个会话期间持久存在的cookie,即使域名发生变化也是如此。因此,通过domain: "lvh.me"解决了开发中的问题。这会创建一个位于不同子域之间的cookie。

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

答案 1 :(得分:59)

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

  

“您要注意的部分是,如果您设置:domain =>   :所有人都喜欢在某些地方推荐,除非是这样,否则就行不通   你正在使用localhost。 :所有默认值都是TLD长度为1   意味着如果您使用Pow(myapp.dev)进行测试,它也无法正常工作   因为这是一个长度为2的TLD。“

换句话说,你需要:

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

清除Cookie也是一个好主意

答案 2 :(得分:21)

我一直在寻找一种方法来解决这个问题,而不必明确说明域名,所以我可以在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

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

答案 3 :(得分:16)

由于某些原因,用域名替换:all对我来说不起作用(rails 3.2.11)。它花了一块自定义中间件来修复它。该解决方案的摘要如下。

tl; dr:您需要编写自定义的Rack Middleware。您需要将其添加到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步:添加自定义中间件类

这就是Rack Middleware的用武之地。一些相关的机架& rails资源:

以下是您应在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 autoload

首先要确保您使用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"

请注意前面的点是必要的。有关原因,请参阅“sub-domain cookies, sent in a parent domain request?”。

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

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

lvh.me技巧映射到localhost。这很棒。有关详细信息,请参阅this Railscast about subdomainsthis note

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

答案 4 :(得分:16)

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

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

答案 5 :(得分:4)

Rails 4.x (对于Rails 5版本也应该没问题)

How to get lvh.me:3000 and subdomain in localhost (Rails)

简单地说,我已共享Cookie以将.lvh.me添加到session_store.rb

它将在localhost admin.lvh.me:3000lvh.me:3000上的子域名之间共享,等等......

#config/initializers/session_store.rb

if Rails.env.production?
    Rails.application.config.session_store :cookie_store, 
                      key: '_app_name_session', domain: ".domain_name.com"
else
    Rails.application.config.session_store :cookie_store, 
                      key: '_app_name_session', domain: '.lvh.me'
end

答案 6 :(得分:3)

你试过吗

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

基本上我们说基础域有单个cookie而只是忽略子域。虽然这种方法仍有一些缺陷......

答案 7 :(得分:0)

支撑轨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/How-To:-Use-subdomains

答案 8 :(得分: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