跨域单点登陆方案实现

SSO英文全称Single Sign On,单点登陆。当咱们搜索单点登陆的时候,会发现不少的文章,然而这些文章通常都是基于一种通用的场景描述,一般在各自的业务环境会更加复杂。在本篇文章,我将描述具体场景下实现单点登陆的方案。html

SSO英文全称Single Sign On,单点登陆。SSO是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。它包括能够将此次主要的登陆映射到其余应用中用于同一个用户的登陆的机制。它是目前比较流行的企业业务整合的解决方案之一。前端

背景

企业发展初期,通常一个域名站点即可承载独立业务。但随企业对于新业务的探索,便都会申请一个新的域名用于这部分新业务的功能承载,一方面是为了作区分,另外一方面是知足监管的要求。而且须要在这种模式下打通原先站点的用户体系。新的独立域名主要有两种:webpack

  1. 原有站点是www.a.com,新独立域名是new.a.com;
  2. 原有站点是www.a.com,新独立域名是www.b.com;

对于第一种场景,通常咱们采用共享session的方式就能够作到用户在不一样域名之间跳转而无需重复登陆。具体实现主要是将cookies中关于用户登陆态的sessionid的domain设置为.a.comweb

这种场景较为简单,实现上作好新老模式之间的切换便可。由于默认状况下sessionId的domian是www.a.com,若是以前已经访问过www.a.com站点,且登陆的时候未清除掉domian为www.a.com的sessionid,那么访问www.a.com的站点,浏览器会把两个同名sessionId传递到服务端,因为是key-value的形式,服务端没法分辨哪一个是新的,哪一个是旧的,若是取了旧的,那么就没法获取用户此时登陆状态。解决这个问题,只须要在设置sessionId的时候把原有domian为www.a.com的置为过时,或者用一个新的sessionId键便可。具体查看set-cookies介绍redis

这种场景在咱们的移动站点用的比较多。例如主站www.a.com,移动站点为m.a.com这种场景。npm

#设置domian为.a.com的sessionId
Set-Cookie: sessionId=a3fWa; Domain=.a.com; Secure; HttpOnly

#将domian为www.a.com的sessionId置为过时
Set-Cookie: sessionId=a3fWa; Domain=www.a.com; expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly
复制代码

接下来咱们主要描述第二种场景。对于这种场景,主要要求是用户无感知,须要作到如下几点:跨域

  1. a站点登陆以后往b站点同步登陆态;
  2. 访问b站点无需登陆页面后,须要主动同步a站点登陆态;
  3. 访问b站点须要登陆页面时,须要跳转到a站点作登陆态同步;

流程一:a站点登陆以后往b站点同步登陆态

登陆以后同步登陆态时序图

以上是主动同步登陆态的时序图。图示中的ticket主要存放在redis中,你也能够存放在其余的存储媒介甚至应用运行内存,可是须要注意的一点就是ticket应一次有效,用过以后须要清除掉。因为这里咱们的b站点也在本身的受控范围,而且redis的读写性能也至关优越,因此a和b链接并读取了同个redis。若是b站点不在受控范围内,可在b站点后台发起一个请求到a站点询问ticket的有效状态。具体流程以下:浏览器

  1. 用户访问a.com的登陆页;
  2. 输入用户名密码登陆,a.com后台校验用户,成功以后生成a站点的sesion并生成一个ticket放入redis中;
  3. 登陆页面登陆成功以后,拿到ticket往b.com发送一个跨域请求(JSONP或者Image);
  4. b站点获取到ticket以后,检验在redis是否存在,存在着设置b站点session并删除ticket;
  5. 跨域请求返回以后继续其余操做,如跳转用户中心,首页等。

流程二:b站点无需登陆页面主动同步a站点登陆态

b站点无需登陆页面主动同步a站点登陆态时序图

因为a、b站点相互独立,假设各自的session过时时间为半小时,若是a站点一直处于访问状态,那么session会一直续命下去,可是b站点因为超过30分钟没有访问,session状态已通过期,这时候访问b站点就会有这个场景了。具体流程以下:安全

  1. 用户访问b.com无需登陆页面;
  2. 若是当前站点用户未登陆,发起异步JSONP请求到a.com;
  3. 若是a.com未登陆,不作任何操做。若是已经登陆,跟上个流程同样,生成ticket信息;
  4. 拿到ticket以后,请求b站点同步登陆状态,b站点生成session;
  5. 同步成功以后主动从新刷新当前页面。

流程三:b站点需登陆页面主动跳转到a站点作登陆态同步;

b站点需登陆页面主动跳转到a站点作登陆态同时序图

同流程二,这个场景的出现也是由于长时间未访问登陆b站点致使,与流程二不一样的是,这个场景是302直接跳转同步页面的方式,由后台直接判断,适合后台直出页面,若是是纯静态页面请使用流程二,具体流程以下:bash

  1. 用户访问b.com须要登陆页面,返回302跳转到a站点的状态同步页面;
  2. a站点状态同步页面判断站点登陆状态,未登陆状态跳转到a站点的登陆页面,登陆流程同流程一,登陆成功以后跳转b站点需登陆页面;
  3. 已经登陆状态作JSONP登陆状态同步到b站点,b站点生成session;
  4. 同步b站点成功以后跳转到b.com站点须要登陆页面。

总结

以上流程主要在于实现SSO过程的针对各类场景的解决方案,根据经过发起方的不一样又可分为两类,主动同步和被动同步。主动同步是向认证站点获取ticket并同步自身登陆态,被动同步是由认证站点向当前站点同步登陆态。

通常网上的资料会有一个专门用做认证登陆的站点,好比a.com和b.com站点都从sso.a.com获取认证状态。其实在本文中就是把a站点用做sso站点了,原理上是一致的。

为了这一套方案落地的时候,业务开发同事无需关注这部分实现细节并编写相应的同步代码,咱们把他写入了总体框架里面,主要作了一下两件事:

  1. 封装前端登陆脚本(支持npm引用,直接webpack打包),登陆以后去作主动站点同步,业务同事在须要登陆的场景,直接调用方法并在回调函数作其余操做;
  2. 使用ejs或者pug等模板引擎,将流程二的同步过程放入通用模板,其余页面在通用模板基础上建立。

更多关于sso认证方面的能够参考OAuth2的流程,这套流程用于不可信站点之间的认证在安全方面会更加成熟些,也是目前微信采用的认证流程。

FYI:

单点登陆(SSO)看这一篇就够了
SSO单点登陆三种状况的实现方式详解
微信网页受权

相关文章
相关标签/搜索