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

Posted

技术标签:

【中文标题】在 Rails 中的子域之间共享会话(cookie)?【英文标题】:Share session (cookies) between subdomains in Rails? 【发布时间】:2012-05-11 06:42:51 【问题描述】:

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

我的 /cofig/initilizers/session_store.rb 文件中有这个:

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

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

【问题讨论】:

【参考方案1】:

如果您使用 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 

【讨论】:

【参考方案2】:

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

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

开发:我已经共享cookie来添加.lvh.mesession_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

【讨论】:

【参考方案3】:

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

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

【讨论】:

谢谢老兄..我的一个项目一直面临这个问题..终于找到了解决方案.. 确保在所有应用程序中使用相同的config.secret_key_base,否则将无法解码 cookie。 我没有看到任何与 Rails 4 相关的问题。你知道这是否已经改变。我无法让它与我的项目一起使用。它不断地重新创建 cookie。谢谢。 如果我想使用CacheStore 将会话存储在memcached 中怎么办? 使用 Rails4,我发现这仅适用于带有破折号但不带有下划线的子域:Appname::Application.config.session_store :cookie_store, key: '_appname_session', domain: :all, tld_length: 2【参考方案4】:

支持rails5

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

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

【讨论】:

【参考方案5】:

我正在寻找一种无需明确说明域名即可解决此问题的方法,因此我可以在 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

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

谢谢,这终于帮助我理解了为什么这么多答案都推荐 tld_length 为 2,但为什么我不需要! 这个答案需要更高。谢谢,先生。 “lvh.me 作为一个类似于 com.au 的***域名”顺便说一句,Rails 确实应该以同样的方式解释 .me,因为它也是一个国家域(黑山)。【参考方案7】:

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

“这里你要注意的部分是,如果你设置 :domain => :all like 在某些地方是推荐的,它根本行不通,除非 你正在使用本地主机。 :all 默认为 TLD 长度为 1,即 意味着如果您使用 Pow (myapp.dev) 进行测试,它也不会工作 因为那是一个长度为 2 的 TLD。”

换句话说,你需要:

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

清除 cookie 也是一个好主意

【讨论】:

这是最好的答案,因为一个更改适用于所有环境(app.com 和 app.dev)。不需要自定义中间件。清除 cookie 也很好! 你错过了, :tld_length => 2 确保在所有应用程序中使用相同的config.secret_key_base,否则将无法解码 cookie。 :domain => :all 在 Rails 4 中不起作用,试试domain => 'lvh.me', tld_length = 2。它对我有用 在 Rails 4.2 中,我在使用 domain: :all, tld_length: 2 域时得到了很好的结果。【参考方案8】:

由于某种原因,用域替换 :all 对我来说不起作用(rails 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.comyourdomain.com会有单独的条目

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

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

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

Railscasts about Rack Railsguide for Rack sesssions abstractly 和cookie sessions 的机架文档

这是一个自定义类,您应该将其添加到 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 subdomains 和this note 了解更多信息。

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

【讨论】:

有没有办法让它与多个***域一起工作?我有一个在不同国家运行的产品。在这里,我们假设默认域是 yourdomain.com,但如果它应该适用于 .be .sv .fr .com.br .com.ar 等呢?谢谢。 我无法让它工作。我正在 Rails 4 中开发,似乎 rias 只是轻轻地忽略了上面的所有代码。它只是不想跨子域共享会话。 @OleHenrikSkogstrøm 确保在所有应用程序中使用相同的config.secret_key_base,否则将无法解码 cookie。【参考方案9】:

你试过了吗

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

)

基本上我们说的是基域只有一个 cookie,只是忽略子域..虽然这种方法仍然存在一些缺陷......

【讨论】:

以上是关于在 Rails 中的子域之间共享会话(cookie)?的主要内容,如果未能解决你的问题,请参考以下文章

在 Rails 3 中跨多个子域删除会话 Cookie

在 Rails 和 Spring MVC 之间共享会话

如何在 Spring Security 中的子域之间共享会话

跨子域访问会话(Rails 4)

是否可以在子域之间共享 cookie

在根域和一个特定的子域上设置会话 cookie