咱们用大楼,保安,请求进入者,小纸条,来访者登记表这个场景比喻,整理了cookie(http://www.javashuo.com/article/p-tlmdohfp-ko.html)和session(http://www.javashuo.com/article/p-fgljnokt-s.html)。假设咱们如今遇到一张这样的状况,就是G集团很大,它有不少幢大楼,按照以前的方式,来访者进入#1号大楼要校验登记,进入#2号大楼也要校验登陆...以此类推,它须要在每一幢大楼里面都校验,而且登记一次,这样手续很繁琐,并且效率很低。那有没有一种方法,让我只要校验登记一次,就能够在整个集团内的全部大楼里畅行无阻呢?这就涉及到单点登陆了。java
英文Single Sign On, 简单来讲,就是在一个多系统的环境中,用户在其中一个系统登陆后,在进入其余系统的时候,不须要再次进行登陆,而是直接拥有登录状态。这种多系统,主要是为了合理利用资源,下降耦合度,把原系统拆分红多个子系统而成的。好比,淘宝和天猫,它们就实现了单点登陆功能,用户在淘宝登陆后,进入天猫页面,就直接有了登陆态。数据库
众所周知,HTTP是无状态的写一,这意味着服务端没法确认用户的信息,因而有了cookie。这样,用户在后续的请求中都带上session_id,从而让服务端识别当前会话,以及当前会话的用户。固然,除了session_id以外,还可使用token来区分。也就是在登陆时,生成一个token,这个token能够保存在数据库中或者缓存中,对应到用户信息(好比以token为key, uid为value),而后把token写入cookie,以后的每次请求都带上cookie,而后经过cookie中的token来获取用户信息。跨域
固然,cookie中的一些敏感信息要注意加密,使用的时候,传到服务端去解密。浏览器
登陆意味着写入,那么注销登陆意味着删除,记住我 意味着关闭浏览器后,在一段时间内打开浏览器,仍然保持登陆状态。缓存
A系统的session存在A系统中,B系统的session存在B系统中,不互相共享,这样就不能共用同一个session_id了。安全
有如下几种方案:服务器
3.1.1 会话复制: 能够作成session集群,而后集群内部彻底同步,这样集群内部每个节点都拥有集群全部节点的数据。可是这样会影响集群的性能,不建议使用。相似于全部大楼都存全部大楼的登记表。cookie
3.1.2 会话保持:能够根据IP作Hash映射的负载均衡,这样同一个IP会一直访问同一台服务器。这样有一个问题,就是对服务器的稳定性要求高,若是出现宕(dàng)机,会有一大部分请求没法命中,迁移到其余机器。相似于一个总的登记中心,有不少个窗口,每一个窗口都有去全部大楼的班车,可是每一个窗口只处理一部分访问请求。session
3.1.3 会话共享: 将Session数据放到缓存中,如Redis,使用Redis模拟Session. 核心思想就是负载均衡
3.1.3.1 让客户端访问同一个sessionId
3.1.3.2 让全部域名对应的服务器访问的Session的数据的位置必须一致
相似于全集团共用同一个登记中心,每一个大楼均可以直接去读写这个登记中心的数据。
咱们能够把登陆功能单独抽取出来,作成一个SSO子系统。其余的子系统登陆时,请求SSO进行登陆,若是已经登陆,则直接放行,若是没有登陆,则执行登陆,SSO系统生成一个session_id或者token, 存入Redis, 返回时将session_id或者token放入cookie中,之后其余系统访问时带上cookie,拦截器获得session_id/token,判断是否存在,是否过时,进而判断是否已登陆。
咱们知道,Cookie中经过Domain域和Path路径来控制Cookie的使用范围。当浏览器访问A网站/系统时,会把A网站/域名的Cookie带过去,访问B时,也只会把B的Cookie带过去,因为域名不一样,不会把其余域的Cookie带过去。
分几种状况
3.2.1 属于同一个顶级域名下的多个子域名,能够在设置Cookie时,讲Domain设为顶级域名,这样在访问顶级域名下的任何子域名时,浏览器都会带上该Cookie的键值对,就能共用同一个Cookie了。
3.2.2 跨多个顶级域名。这种状况有点复杂。
3.2.2.1 因为站点A(www.A.com)不能读取到由站点P(www.P.com)建立的加密ticket,因此当用户访问A站点上须要登陆才能访问的资源时,A站点会首先查看是否有A-ticket。 若是有且没有过时,则直接使用。
3.2.2.2 若是没有或者已过时,则证实用户没有在A站点登陆过或者已经失效,不过并不保证用户没有在B站点登陆,(重复一下,既然是单点登陆,固然不管你在A,B任意一个站点登陆过,另一个站点都要能够访问),请求会被重定向到P站点的验证页面.
3.2.2.3 校验P站点的Cookie, 若是存在且没有过时,则直接重定向回站点A,同时将验证信息Session_id/P-ticket返回给站点A。
3.2.2.4 若是P站点没有Cookie或者已失效,则跳转到Login登陆页面,输入登陆信息,登陆页面完成登陆后,生成一个P站点的Session_id/P-ticket,写入一个加密cookie,而后重定向到A站点的登陆处理页,并把加密的用户信息做为参数传递给这个页面,这个页面接收登陆页的用户信息,解密后也要写一个cookie,也就是A-ticket,从此用户再次访问A站点上须要登陆权限才能访问的资源时,只须要检查这个A-cookie是否存在就能够了。
3.2.2.5 当用户访问B站点时,会重复上面的过程,监测到没有B-ticket,就会重定向到P站点的验证页面,去检查P-ticket,若是没有,就登陆,有则返回B的登陆处理页面写B-ticket。
3.2.2.6 注销的时候须要删除P-ticket和A-ticket。
3.2.2.7 怎么删除cookie: 简单的说实际上是建立一个和你要删除的cookie同名的cookie,并把cookie的expire设为当前时间以前的某个时间,不过在跨子域的删除cookie时有一点要注意:必需要把cookie的域设置为父域.
3.2.2.8 为了保证各个环节的传输的安全性,最好使用https链接。
到这里,咱们已经能够实现单点登陆了。
说到单点登陆,就确定会见到这个名词:CAS (Central Authentication Service),下面说说CAS是怎么搞的。
若是已经将登陆单独抽取成系统出来,如今咱们有两个系统,分别是www.java3y.com和www.java4y.com,还有一个SSO认证中心: www.sso.com
3.3.1 用户想要访问系统Awww.java3y.com受限的资源(好比说购物车功能,购物车功能须要登陆后才能访问),系统Awww.java3y.com发现用户并无登陆,因而重定向到sso认证中心,并将本身的地址做为参数。请求的地址以下:
www.sso.com?service=www.java3y.com
SSO认证中心发现用户未登陆,将用户引导至登陆页面,用户进行输入用户名和密码进行登陆,用户与认证中心创建全局会话(生成一份Token,写到Cookie中,保存在浏览器上)
认证中心重定向回系统A,并把Token携带过去给系统A,重定向的地址以下:
www.java3y.com?token=xxxxxxx
接着,系统A去SSO认证中心验证这个Token是否正确,若是正确,则系统A和用户创建局部会话(建立Session)。到此,系统A和用户已是登陆状态了。
3.3.2 此时,用户想要访问系统B www.java4y.com受限的资源(好比说订单功能,订单功能须要登陆后才能访问),系统B www.java4y.com发现用户并无登陆,因而重定向到SSO认证中心,并将本身的地址做为参数。请求的地址以下:
www.sso.com?service=www.java4y.com
注意,由于以前用户与认证中心www.sso.com已经创建了全局会话(当时已经把Cookie保存到浏览器上了),因此此次系统B重定向到认证中心www.sso.com是能够带上Cookie的。
认证中心根据带过来的Cookie发现已经与用户创建了全局会话了,认证中心重定向回系统B,并把Token携带过去给系统B,重定向的地址以下:
www.java4y.com?token=xxxxxxx
接着,系统B去sso认证中心验证这个Token是否正确,若是正确,则系统B和用户创建局部会话(建立Session)。到此,系统B和用户已是登陆状态了。
看到这里,其实SSO认证中心就相似一个中转站。