在Rails 3中跨子域共享登录会话的正确方法?

时间:2011-02-25 22:17:43

标签: ruby-on-rails-3 authentication

我在互联网上发现的有关此内容的所有信息均表示使用类似

的内容
Login::Application.config.session_store :cookie_store, :key => '_login_session', :domain => '.domain.com'

对我想要分享该会话的所有子域使用相同的密钥。当我这样做时,子域之间不会传递身份验证。事实上,当我访问任何所谓的共享会话时,初始会话被覆盖

即。在login.domain.com上,我运行身份验证,返回用户名和会话user_id。然后我去sub.domain.com,它应该返回与login.domain.com相同的信息,但不会。在此之后,我回到login.domain.com,我也不再在那里进行身份验证。

在sub.domain.com上,session_store.rb文件如下所示:

Something::Application.config.session_store :cookie_store, :key => '_login_session', :domain => '.domain.com'

我用过:所有的:域值,也有相同的结果。如果我删除上面的:domain设置,那么初始会话不会被覆盖,但它也不会被共享。

当我查看Firefox Cookie编辑器中的cookie时,两个子域都使用相同的cookie名称,但未共享身份验证。这是一个非常基本的Users表,我使用OpenID和OAuth来执行Omniauth

的身份验证

3 个答案:

答案 0 :(得分:6)

更新:建议的解决方案毕竟不是那么难看,广告交易平台和DSP / SSP使用相同的技术来交换访问者的会话ID,以便他们可以更好地通过广告定位访问者(下次访问者再次在他们的网络中弹出)


如果您可以规避浏览器跨域屏障,则可以执行此操作。例如,JSONP专门为此目的而构建。是的,会话信息总是集中存储,否则如果您收到会话ID为“zigzag”的请求,您如何检查它是否有效?

在login.domain.com上进行身份验证的“那些”网站可能使用ajax代理,或使用其他方法来解决跨域问题。

最古老的“技巧”是在应用程序中创建一个看起来像图像的钩子,因为图像可以从任何地方加载。

例如,在 login.domain.com 上,您对用户进行身份验证,发送到服务器并返回响应,并且cookie将存储在 login.domain.com < / em>使用会话ID(也存储在服务器中)。然后 - 从Javascript - 你得到一个图像,附加会话ID,如http://any.domain.com/path/image.jpg?sessionID=abcd - &gt;在回复中发回的任何Cookie都将存储在 any.domain.com

另一个解决方案 - 与前一个一样丑陋 - 是使用隐藏的iframe来调用any.domain.com(当成功进行身份验证时),该请求将返回响应,它的cookie将在any.domain.com域下编写。

如果您有多个子域,并且可能会使您的架构复杂化,我强烈建议您创建一个代理,并使其可用于同一IP地址上的每个子域。然后,无论用户进入何处,对于每个子域,身份验证过程始终都是相同的。

答案 1 :(得分:6)

由于某种原因,为域添加点前缀不起作用(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的用武之地。一些相关的机架&amp; 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

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

答案 2 :(得分:1)

使用上面提到的行将Rails会话配置为跨子域共享后,一旦进入子域,就可以通过Javascript将document.domain属性设置为域来从域中访问cookie。 / p>

假设您在mysubdomain.domain.com,您将设置: document.domain =“domain.com”; 现在,您将能够从domain.com访问cookie。

(请注意,由于原始策略相同,您无法将document.domain设置为任何其他子域)