单点登录并不是一个新鲜的玩意儿,比较官方的解释是企业业务整合的解决方案之一,通俗来讲SSO就是一个通用的用户中心,国内比较流行的UCenter就是一套单点登录解决方案。而近期以CSDN明文存储用户密码并泄露用户信息开始的各大网站争先恐后的泄露自己的用户数据库除了暴露了这些网站的良心和智商外,如何设计用户中心已成为架构师们的热点话题之一。在最近一两年的项目经验中有幸接触到各种平台的单点登录系统的开发,所以借此机会总结下B/S架构的单点登录系统的开发经验。
单点登录系统的类别
就目前比较流行的应用来看,单点登录系统主要分为三种类型:一种是基于oauth协议的网络令牌(我是这么叫的),一种是基于Web Service或者简单Http协议实现的Passport机制,还有一种是以openid框架形成的通用账号登录机制。其中,基于oauth协议主要应用在网站外部,比较知名的有Google Account、Facebook Connect和新浪微博链接等;Passport的应用主要是针对同一网站内不同架构不同平台,知名产品则更是数不胜数,例如Google Account账号基本上可以应用全部Google的网站,而国内各大门户无数的应用系统也都只需要一个用户通行证就能畅通无阻;至于OpenID这样会共享用户信息的应用,在国外可能会很火,在国内这样一个用户一寸金的利益集团面前可能会被采纳,所以下面的论述也主要针对前面两种。
单点登录系统的优点
对于用户来讲,最理想的情况下一个账号和密码就可以横行整个互联网,当然这是不可能的。不过现实中稍微有点价值的网站基本上都可以支持各种oauth协议的单点登录系统,所以能使用微博账号、SNS账号或者豆瓣账号登陆的将会为用户带来更多的方便。
对于企业来讲,尤其是业务繁琐复杂的大企业,单点登录系统可以让他们不必为每一个应用都开发用户系统,从而大大降低了工作量,而且统一的用户数据在后期管理和维护中也会更加方便。而基于oauth协议的单点登录系统虽然开发成本高昂,但是能为企业带来最具有价值的第一手用户信息,其收益可观。
单点登录系统的基本流程
如上图所示,不管是哪种方式的SSO系统其功能流程大概都是这样:应用系统只需要调用SSO的验证接口验证当前用户是否为已登录用户,如果是未登录用户或者严重不合法则跳转到SSO系统。此时已注册用户可以直在SSO成功登录后返回应用系统,应用系统开始重复上述步骤;而未注册用户则需要先注册或者使用第三方SSO初始化账号,继而重复上述步骤。
不同的是,账号链接这样的SSO系统在登陆成功后返回给应用系统一个key值、用户uid和其他信息,而应用系统一般还是需要在自己的用户系统中新建一个用户账户并且建立一个映射关系来关联SSO系统的返回值。如下图所示是实现绑定微博账号登陆的数据库模型图,其中应App_Users表可以实现一个用户绑定多个单点登录账号,并且这样的用户是不需要再应用系统中存储密码等不必要的信息的。而Passport机制的SSO系统基本上处理所有用户登录、注册、验证的行为,其他应用系统只需要按需取用即可。
单点登录系统开发难点
从上述功能流程可以看出,使用基于oauth协议的单点登录系统应该是比较好的解决方案,但是没有谁愿意把用户的掌控权拱手让给别人,所以国内很多中小网站就算是没有能力开发自己的passport单点登录系统,也会去选择UCenter这样第三方开源的单点登录系统。而Passport机制的单点登录系统都会面临这么一个问题,如何跨域传递cookies,至于为什么使用cookies、为什么要跨域这么啰嗦的废话我就不多写了,常用的跨域传递cookies的方法有三种:javascript/iframe、header和借助数据库。
javascript/iframe的思路是将单点登录种下的cookie动态传到应用系统,在ajax/jquery广泛应用的时代javascript的是很常见的解决方案,例如UCenter的做法就是在单点登录系统登陆后使用一段javascript动态把cookie传递到所有设置为同步登录的应用,而iframe因为存在诸多弊端不建议使用。
header的解决方法是使用P3P机制进行跨域传输,但是IE必须用加P3P的头信息才可以跨域。看来IE对跨域的限制更为严格!所以要想实现完全跨域,在header头里面加上这一句话最好:header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA div COM NAV OTC NOI DSP COR"');。不过在以前使用P3P协议开发单点登录系统的时候或多或少都有些莫名其妙的问题,看来还是掌握的太少了。
数据库存储,还是以Ucenter为例,在Discuz的cdb_sessions表就是一个结合session实现的跨域传递session的功能,只不过这样的功能会给数据库带来严重的I/O负担,但凡有点流量再碰上点极品的用户这样的数据库性能肯定要被严重拖累。
在我实际开发过程中,一般这样解决单点登录系统的跨域传递cookie问题:首先验证用户来源是否在我们的站点列表内(如果是首先进入SSO则略去这一步),其次验证用户合法性,用户通过验证后,加密cookie和公用密钥后的值将会在用户登录之后作为参数一起跳转回来来源的应用系统,应用系统获取到cookie值后调用SSO接口进行解密、验证等工作。此外,还有一种比较简略的SSO设计方案,所有SSO系统应有的功能都以API形式呈现,接受被授权的应用系统的请求并且返回相应的XML或者Json结果,不同的是,这样做需要应用系统最起码的有自己的登陆和注册页面,适用于同步不得不使用自己用户功能的已经成型的外部应用系统。
此外,在上一篇文章中讨论过的用户密码加密问题,由上图也可以看出,pass字段存储的应该是用户真正的密码经过md5和加上salt值(随机字符串)再次md5,也就是md5(md5(realpass).salt),这个是程序猿应该知道的最基础的加密方式。此外,很多人容易忽略的一个地方是cookie校验,至少我亲身体验了京东修改完用户密码以前种下的cookie依然可以使用,Firebug看了下京东存储的Cookie虽然是加密的,但是明显的验证cookie只是验证了cookie是否为空或者是否可以解密,而完全没有去验证cookie用户是否合法,这样如果知道了京东的加密算法,即便是不知道用户密码,也可以伪造用户cookie进入用户账户。
至于网上很多企业给出的SSO解决方案,我就不喷了,MD,如果真要用,UCenter或者简单的使用新浪、腾讯、豆瓣的网站链接都不错,虽然UCenter问题很多(这个以后再喷),但是最起码的企业对用户具有完全的掌控权。
http://www.itokit.com/2012/0523/74123.html