如何实现 [ 跨域单点登录 ] ?

jKey
  • 506

管理用户网站

  • 用户账户网址:http://accounts.com
  • 用户登录网址:http://accounts.com/login
  • 用户等处网址:http://accounts.com/logout

应用网站
  • 应用1:http://app1.com
  • 应用2:http://app2.com

实现场景
  1. 登录 应用1,跳转到 http://accounts.com/login 进行登录
  2. 登录成功跳转 回应用1,地址后面会带个token,例如:http://app1.com/callback?token=xxxx 来验证登录
  3. 浏览了 应用1 后,用户进入 应用2 (用户可能会自己输入 app2.com 的地址访问 应用2)
  4. 此时 应用2 通过“某种方式”来判断之前 应用1 的登录与否,来设置当前用户是否登录

问题是如何实现场景中的 第4条?


我现在想到的是:
  1. 同步登录:像 discuz 那样,在登录一个应用时对其它应用进行同步登录,缺点是登录后最好是等个几秒在跳转使其它应用能够有足够时间写下 cookie,但是我不喜欢“要等几秒”的方式
  2. 使用 jsonp:虽然知道怎么用 jsonp 进行跨域请求,但是实现起来有些困难,而且处理不好就有安全问题

不知各位有什么好的方法???


----------------------- 华丽的分割线 ---------------------------

下午花了点时间分别研究了一下两组网站的登录方式:


  1. google.com 和 youtube.com
  2. taobao.com 和 etao.com

一、 google.com 和 youtube.com
情况一:都未登录,但是浏览过 youtube
  1. 登录 google.com 通过 https://accounts.google.com/ServiceLoginAuth 验证用户名密码
  2. 通过验证后跳转到 https://accounts.google.com/CheckCookie 检查是否有浏览过其它应用,比如说 youtube,这时就再次跳转到 https://accounts.youtube.com/accounts/SetSID 进行登录
  3. 最后再跳回 google.com 的首页

情况二:已登录 google.com,但未浏览过 youtube 也并未登录
  1. 由于登录了 google.com 所以我直接从地址栏里输入地址进行第一次访问 youtube
  2. 进入 youtube 首页显示的是我需要登录,故点击登录
  3. 依然跳转到 https://accounts.google.com/ServiceLoginAuth,但是不用任何输入直接跳回 yutube 首页

二、 taobao.com 和 etao.com
  1. 不管我有没有登录 taobao.com,我进入 etao.com 首页,首先会检查 cookie 是否登录
  2. 登录则 etao.com 首页显示登录状态
  3. 未登录则跳转到 http://jump.taobao.com/jump?target=http%3A%2F%2Fwww.etao.com%2F%3Ftbpm%3Dt 来检查在 taobao.com 是否有登录
  4. 登录则跳回 http://pass.etao.com/add?... 进行登录后在跳回首页,登录状态为登录
  5. 未登录则依然跳回首页,登录状态为未登录

现在基本知道怎么去做了,或者有其它更好方法的?说出来大家讨论一下……

回复
阅读 29.2k
10 个回答

研究并看过sohu的,拿出来分享。

统一在 login.common.com?return=www.a.com 登录,(带需要返回的地址)
if 验证成功 return js,在login.common.com/success 加入返回的JS,并显示,然后跳回 return 参中带的值。

那么这段JS内容是什么样的呢?
就是通过JS的方式调用各个站点,生成各自的cookie。

目前 ucenter 也是采用这种方式。 登录中心,应该要有一个类似应用列表这样的东西。

我知道的有下面三种方法:

1. 页面嵌套 JS 跨域请求, 就像康盛的 UCenter 产品一样;
2. 让页面依次跳转不同应用的域名来解决跨域问题;
3. Google 一样的, 用户主动请求;

下面是详细:
1. app1, app2, app3 挂接在 UC 上, 用户请求 app1 登录, app1 程序在验证通过同时请求 UC 需要同步登录的代码(如下), 打印到页面上.

<script type="text/javascript" src="http://app2/api/uc.php?time=132
4695020&code=f97bp%2FyiCrfFF%2B4lIndT1k3PoVk5%2BmVvNJMYiBzq7ssWE8FS
smj7T3jujWyDpKpT2t8vO7o5M19FlzvNdihXMDWqvyoko9C3lkpG%2BQX57zL112gb
GH%2FPwS9YD9A3%2BQVKNFSDzriFOqq9IFWJx6q4x%2Fk%2FMAltJygTiwRZ" 
reload="1"></script>

<script type="text/javascript" src="http://app3/api/uc.php?time=1324
695020&code=2b55qIaEphMxdvEYZJok8GEoDW%2BctvZeWS84LvaH8MHf8AzrRXiLD
ZFIvS4cmUkXAGGeDnSVQ9rsu62iDnSAUSTEdSH2%2Bup5xZFvtgl9KRMEcOLLySih2w
vNL%2BSjRQgqErT5kEK0878zxlhRQn0ypgxXgmoCY3zE87sy" reload="1"></script>

注意的是, uc.php 需要 p3p header

header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');

2. app1, app2, app3, 用户请求 app1 登录, app1 程序在验证通过的时候让带着参数让浏览器跳到 app2.com (在输出页面的时候加上一段js, 代码类似下面), 然后再跳到 app3.com ... 依次种上 cookie 最后跳回 app1.com

<script type="text/javascript" reload="1">window.location.href=\'app1.com
/loginAuth.phpcode=ctvZeWS84LvaH8MHf8AzrRXiLDZFIvS4cmUkXAGGeDnSVQ9rsu62i
DnSAUSTEdSH2%2Bup5xZFvtgl9KRMEcOLLySih2wvNL%2BSjRQgqErT5kEK0878zxlhRQn0yp
gxXgmoCY3zE87s'\';</script>

坏处是 - 在用户看起来, 页面会有好一会儿的白屏! 不适合多个应用.

3 . Google 的方法, 坏处是用户得主动的点击登录按钮, 没法做到无缝的同步登录, 好处是适合多个应用;

哪一个好, 得看什么要求, 如果不需要做到无缝的同步登录的话, 肯定是使用 Google 的方案!

google的这个办法应该是一个比较完整的流程了,但是还是有些缺点,那就是在我没有访问youtube的时候登录了google,它没有给我自动登录到youtube,还需要你手动点击登录。虽然不用输入密码啥的,但也有些麻烦。

其实可以做一个改进,就是你在youtube里挂一个google域的js,当然这个js实际上是通过动态脚本输出的。我们在脚本里先检查用户时候已经在google域里登录过了,如果已经登录,那么就动态输出让它在youtube域里也登录的js。

这个好处虽然只省了一步,实际上是把你在问题中的第一种方法写cookie的时间分散到访问各个子站去完成,可以让用户几乎感觉不到。

app2.com 同样带上 ?token=xxx 不就好了, 如果是从 app1.com 的 link 跳转过去

我有一个不太有“对称完美”的解决办法,当app多的时候可能比较麻烦,但也算一个办法:

在app1.com的callback页面最后,加上script标签

<script src="http://app2.com/callback=<?php echo $token ?>" ...

这样可以让app2也登陆,不过你也看得出来,如果app比较多的时候,你的callback页面都得提到其他的app的callback地址,我说的麻烦就是指这个

最简单的方法就是共用sessionid的方式
登陆完成后将sessionid用跳转或者js的方式传到个子站点,将原来的sessionid改为统一的sessionid
不过这样的缺点是session必须在同一部服务器,或者使用分布式的session存储如memcache,而且站点多的话登陆的时间会相应延长,但对于小站点是个简单的解决方式。

组新版的谷歌浏览器,已经出现了这个bug了,如果本地测试是IP的话,那内联js方法就失效了,我还没测试不同域名下的结果是不是这样的

通过识别cookie或者参考cas

php技术讨论群 323899029

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏