SSO( Single Sign On ),即单点登录,是一种控制多个相关但彼此独立的系统的访问权限, 拥有这一权限的用户可以使用单一的ID和密码访问某个或多个系统从而避免使用不同的用户名或密码,或者通过某种配置无缝地登录每个系统。
与 SSO 交互的2个元素:1. 用户,2. 系统,它的特点是:一次登录,全部访问。SSO 是访问控制的一种,控制用户能否登录,即验证用户身份,而且是所有其它系统的身份验证都在它这里进行,从整个系统层面来看 SSO ,它的核心就是这3个元素了:1. 用户,2. 系统,3. 验证中心。
##1.同一个域名下的所有子域名如何实现单点登录
假如我们的站点是按照下面的域名进行部署的:
a.shiroi.top
b.shiroi.top
这两个站点共享同一域 shiroi.top
那么登录 a.shiroi.top 理应连同 b.shiroi.top 也会一同登录
登录成功以后,生成唯一标识符Token(知道token,就知道哪个用户登录了)。设置 cookie 信息,这里需要注意,将Token存到 cookie 中,但是在设置的时候必须将这 cookie 的所属域设置为顶级域 .shiroi.top 。这里可以使用 setcookie 函数,该函数的第四个参数是用来设置 cookie 所述域的。
setcookie(‘token’, ’xxx’, ‘/‘, ’.shiroi.top’);
访问 a.shiroi.top 系统,浏览器会将 cookie 中的信息 token 附带在请求中一块儿发送到 b.shiroi.top 系统。这时该系统会先检查 session 是否登录,如果没有登录则验证 cookie 中的 token 从而实现自动登录。
a.shiroi.top 登录成功以后再写 session 信息。以后的验证就用自己的 session 信息验证就可以了。
##1.2 退出登录
这里存在一个问题就是 sub1 系统退出以后,除了可以清除自身的 session 信息和所属域为 .shiroi.top 的 cookie 的信息。它并不能清除 a 系统的 session 信息。那 b 仍然是登录状态。也就是说,这种方式虽说可以实现单点登录,但是不能实现同时退出。原因是,a 和 b 虽说通过 setcookie 函数的设置可以共享 cookie,但是二者的sessionId 是不同的,而且这个 sessionId 在浏览器中也是以 cookie 的形式存储的,不过它所属的域并不是 .shiroi.top 。
那如何解决这个问题呢?我们知道,对于这种情况,只要是两个系统的 sessionId 相同就可以解决这个问题了。也就是说存放 sessionId 的 cookie 所属的域也是 .shiroi.top 。在PHP中,sessionId 是在 session_start() 调用以后生成的。要想使a 和 b 有共同的 sessionId ,那必须在 session_start() 之前设置 sessionId 所属域:
ini_set(‘session.cookie_path’, ‘/‘);
ini_set(‘session.cookie_domain’, ‘.onmpw.com’);
ini_set(‘session.cookie_lifetime’, ‘0’);
##2、不同域之间如何实现单点登录
假设我们需要在以下这些站之间实现单点登录
上面的方案就行不通了。不过github上有开源的sso解决方案,实现原理与主流sso差不多。
https://github.com/jasny/sso
**核心原理:**
1.客户端访问不同的子系统,子系统对应的 SSO 用户服务中心使用相同的 SessionId。
2.子系统 Broker 与 Server 间通过 attach 进行了授权链接绑定
- 同根域名不指定 domain 根域名,第一次授权需要每次都要跳转到授权服务,但是指定了domain, 同根域名只要有一个授权成功,都可以共用那个 cookie 了,下次就不用再授权了,直接就可以请求用户信息了。
第一次访问A:
第二次访问B:
##2.1 登录状态判断
用户到认证中心登录后,用户和认证中心之间建立起了会话,我们把这个会话称为全局会话。当用户后续访问系统应用时,我们不可能每次应用请求都到认证中心去判定是否登录,这样效率非常低下,这也是单Web应用不需要考虑的。
我们可以在系统应用和用户浏览器之间建立起局部会话,局部会话保持了客户端与该系统应用的登录状态,局部会话依附于全局会话存在,全局会话消失,局部会话必须消失。
用户访问应用时,首先判断局部会话是否存在,如存在,即认为是登录状态,无需再到认证中心去判断。如不存在,就重定向到认证中心判断全局会话是否存在,如存在,通知该应用,该应用与客户端就建立起它们之间局部会话,下次请求该应用,就不去认证中心验证了。
##2.2 退出
用户在一个系统退出了,访问其它子系统,也应该是退出状态。要想做到这一点,应用除结束本地局部会话外,还应该通知认证中心该用户退出。
认证中心接到退出通知,即可结束全局会话,用户访问其它应用时,都显示已登出状态。