透明用户会话跨多个站点(单点登录+单点注销)

39
我有几个不同域名的网站: example.com、example.org、mail.example.com和passport.example.org 。所有网站都有共同的外观和感觉,并且应该共享相同的用户基础。
在这种极端情况下,我仍希望所有网站都可以“透明地”(尽可能)共享用户会话,具有以下关键属性:
  1. 单点登录。当用户在passport.example.org登录并访问任何其他站点时,应视为已登录。

    已登录用户在站点标题中获得“您好,$username”,以及列出他们可以访问的服务的不同导航菜单。如果未登录,则欢迎语句改为指向passport.example.org/signon的“登录”链接。

    已知可信任的域名列表,因此可以使用OpenID或一些自定义轻量级协议很容易地实现。当用户第一次访问该站点时,我将重定向到passport.example.org的特殊认证终结点,然后在包括身份信息(或“未登录”匿名身份)的情况下将其静默重定向回来。对于大多数浏览器,这是完全透明的。显然,我使用随机值来防止重定向循环。

  2. 单点注销。当用户在任何站点的标题中点击“注销”时,下次访问任何站点时,应将其视为“未登录”状态。

    OpenID并不是为此而设计的。我的当前想法(我已经有了部分工作实现)是发送不是用户身份而是“全局”会话令牌,并在数据库中共享全局会话表(global_session_token ↔ user relation)。

  3. 支持机器人和无cookie用户。网站具有公共区域,应该可以被没有任何cookie支持的用户代理访问。

    因此,我提到的重定向成为问题,因为对于没有接收到cookie的用户代理,它们将被重新定向到登录页面。我的解决方案是通过查询字符串参数在URL中传递会话信息,例如/some/page?session=abc123

每次页面请求,我都会将用户代理传递到验证端点并返回。这不仅会困惑机器人,而且会很快在我的会话数据库中污染出生即死的会话。我绝对不想显示“嘿,你没有启用 Cookie,请离开!”的页面,那会非常粗鲁和令人失望。虽然我需要支持 Cookie 才能登录,但我希望用户可以自由阅读网站内容等,没有任何限制。

此外,我明确地希望除了我提到的一些透明跨域重定向之外,不要将会话 ID 放在 URL 中。我认为这样做存在安全问题,而且通常是一个坏习惯。

目前我已经没有太多的想法了。

好吧,我知道这很难,但 Google 实际上以某种方式使用这个功能(google.com、google. 很多 gTLDs、gmail.com 等),对吧?所以,这应该是可行的。

我将非常感谢任何协议描述的建议(这将是最好的),或者是已经成功实现类似功能系统的链接(无论是可读的代码还是在线网站,供观看和学习)。

总之:多个没有共同根的域,共享用户基础、单点登录、单点注销,匿名浏览时不需要 Cookies。

所有的网站都在同一个网络上(但是运行在不同的服务器上),部分共享相同的 PostgreSQL 数据库(位于同一数据库的不同模式中)。大多数网站使用 Python/Django 编写,但是其中一些使用 PHP 和 Ruby on Rails。虽然我正在考虑一些框架和语言无关的实现方法,但如果指出任何实现方法的话,我将不胜感激。即使我不能使用它们,如果我能了解到如何在那里完成它,也许我就能想出类似的实现方法。


不,这并不难。这只是一个技巧,就像卡牌魔术一样。一旦你知道了方法,就很容易了。 :-) - Wim ten Brink
7个回答

32

好的,让我进一步解释一下。 (所有URL都是虚构的!)正如我所说,访问者转到 http://www.yourwebpage.com 并表明他想要登录。他被重定向到http://your.loginpage.org?return = http://www.yourwebpage.com/Authenticated,在那里他将需要提供用户名和密码。
当他的帐户信息有效时,他将返回到在登录URL中提供的页面,但是还会带有一个额外的参数,该参数将用作ID。因此,他转到http://www.yourwebpage.com/Authenticated?ID = SharedSecret ,其中SharedSecret将是一个临时ID,有效期为30秒或更短。
当调用您的身份验证页面时,页面将调用在yourwebpage.com和loginpage.org之间共享的方法以查找SharedSecret的帐户信息,以检索更持久的ID。此永久ID存储在yourwebpage.com的Web会话中,不应向用户显示。
共享方法可以是任何内容。如果两个服务器在同一台机器上,则它们可以仅访问同一个数据库。否则,它们可能通过Web服务与另一个服务器通信。这将是服务器到服务器的通信,因此用户是否是机器人或没有cookie支持并不重要。用户不会注意到此部分。
您唯一需要处理的是用户的会话。通常,用户将收到存储在cookie中的会话ID,但它也可以作为GET请求的一部分包含在URL中。通过向表单添加隐藏输入字段,将会话ID放在POST请求中更加安全。

幸运的是,一些Web开发语言已经提供了会话支持,因此您无需担心维护会话和发送会话ID。不过这项技术还是很有趣的。您需要知道会话应该始终是临时的,因为有被劫持会话ID的风险。

如果您需要处理不同域上的多个网站,则需要首先进行一些服务器间通信的工作。最简单的方法是让它们共享同一个数据库,但最好在该数据库周围构建一个Web服务以增加额外的保护。确保此Web服务仅接受来自您自己域名的请求,以提高安全性。
当您拥有服务器之间的连接时,用户将能够在您的不同域之间切换,并且只要您将会话ID传递给新域,用户就会保持登录状态。如果用户使用cookie,会话很少会丢失,这将要求重新登录。没有cookie的情况下,如果会话ID在浏览页面之间丢失,那么有可能用户必须重新登录以获取新cookie。(例如,访问者去访问Google,然后回到您的网站。使用cookie可以从cookie中读取会话,而没有cookie则会话将丢失,因为Google不会将会话ID转发。

请记住,在不同域之间传递会话ID是一种安全风险。会话ID可能被劫持,从而使其他人能够冒充您的访问者。因此,会话ID应该具有短期性和模糊性。但即使黑客获得了会话ID的访问权,他仍然无法完全访问帐户本身。除非他直接进入登录页面,否则他将无法拦截服务器间通信,因此无法访问包含您用户信息的数据库。


2
非常感谢您提供如此详细的解释!“并指示他想要登录”——这就是问题所在。实际上,我希望用户被自动识别为已登录,而不需要任何操作。我使用JavaScript JSONP请求(使没有JavaScript的用户点击“登录”链接,但否则似乎无法正确支持机器人)到认证服务器来完成此操作,然后一切都变得简单了。 - drdaeman

5
据我所知,谷歌仅在“Google.com”域名下使用cookie。但是,谷歌还使用OpenID来实现通用的登录机制。基本上,这是通过将您重定向到一个特殊的登录页面来实现的。该登录页面将检测您是否已登录,如果您尚未登录,则会要求您登录。否则,它将直接将您重定向到下一页。
因此,在您的情况下,用户将打开somepage.example.com,该应用程序的会话没有登录ID。因此,它会将用户重定向到logon.example.biz,用户将在那里登录。在该页面背后也会有一个会话,并且该会话将告诉用户已经登录。 (或者没有登录,在这种情况下,用户必须先登录。)然后,它将重定向用户到somepage.example.com?sessionid=something,其中此sessionid将存储在somepage.example.com的会话中。然后,该会话也将知道用户已登录,对于用户来说,这几乎是透明的。
实际上,用户会被重定向两次。

谢谢!唯一的问题是如何处理禁用或不可用cookie支持的机器人和用户。网站(somepage.example.com)有公共部分,应该可以被未登录的用户和机器人访问。每次重定向并创建新的永远不会使用的会话是一个问题。 - drdaeman
1
没有cookie,即使是Google也会抱怨并建议用户打开cookie。否则你的Google账户将无法工作。基本上,Web服务器需要在客户端上记住会话的某些东西。有时你可以通过将会话作为HTML页面的一部分发送并作为postdata返回来解决这个问题,但Google的人只是告诉每个人打开cookie。 - Wim ten Brink
1
关于重定向,您不必在每个页面上都重定向用户,只需在需要额外保护的页面上进行重定向。在您的会话数据中,如果用户已登录,则存储用户ID,对于匿名访问者和机器人则不存储任何内容。所有页面都可以检查您的会话中是否有用户ID。但是,受保护的页面将在用户未知的情况下将其重定向到登录页面,而所有其他页面仅使其保持匿名状态。 - Wim ten Brink

1

对于这个解决方案,不需要护照服务器。

登录

  1. 登录
  2. 使用加密的会话ID和其他信息创建令牌
  3. 显示带有来自所有域的令牌的图像,以设置其上的cookie。

通过cookie进行授权

  1. 您已经在所有域上拥有cookie。

退出

  1. 清除cookie
  2. 销毁会话
  3. 在DB中清除用户ID与最后一个会话ID的关系(我认为您将会话ID保存在用户表中,以便通过cookie提高会话)

我无法尝试此解决方案。但是现在我和你一样遇到了同样的问题(SSO),我明天会尝试这个解决方案。


1

好的,我不使用ASP.NET。网站是用Python/Django(主要)、PHP和Ruby on Rails编写的,但还是谢谢你的建议!我会看一下,如果这正是我想要的,我会尝试找到或创建类似的东西。 - drdaeman

1
如果您的所有应用程序都在一个域上,那么并不太困难,因为您可以使用域级别的 cookie 来处理会话控制。(无 cookie 会话管理很困难,但是可以做到,但是很困难。)
然而,您指出了一些位于 example.com 域和 example.org 域上的站点。
通过我的 Google 登录和其他网站(如 orkut.com)进行一些尝试后,看起来当您访问需要凭据的页面时,它会将您重定向到一个通用站点以进行帐户登录,然后将您重定向回原始站点,可能会在 URL 中传递某种会话令牌。从这个令牌中,orkut.com 服务器可能会直接与 google.com 服务器交流,以验证令牌并获取所需的任何其他用户信息。
关于域级别 cookie 的更多信息如下所述:
如果您有两个站点,例如 foo.example.com 和 bar.example.com,则可以设置特定于主机的 cookie,但也可以为该域设置 cookie,该 cookie 将被该域上的所有主机看到。

所以,foo.example.com 可以为 foo.example.com 设置一个 cookie,它只能被自己看到,但它也可以为域名 example.com 设置一个 cookie,当用户访问 bar.example.com 时,他们也会看到这个第二个 cookie。

然而,如果你还有一个网站,snafu.example.org,它无法看到 foo 设置的这个域级别的 cookie,因为 example.orgexample.com 是两个完全不同的域。

您可以使用域级别的 cookie 在同一域中跨站点维护会话信息。

如何设置域级别的 cookie 取决于您的开发环境(我对 rails 没有经验,抱歉)。


0

我曾经使用过Shibboleth,用于单点登录和联合身份验证场景,但我猜你需要更简单的解决方案。


-4
一个能够跨域的Rails插件来实现这个功能会非常棒。
我想要做到这一点,唯一能想到的方法是强制用户下载一个工具栏,或者一些小型的Air应用程序,以便自动处理登录。
我希望知道另一种方法,因为那样很糟糕。

1
最好使用注释而不是答案。 - DanielV
不符合 OP 要求 - Juanito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接