缘起
- 简称SSO,在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的其它应用系统
- 应用场景 同一家公司的不同子系统之间的登录认证
- 单点登录实现方式
- 基于cookie凭证
- 适用子系统之间主域名一致,如此这般才能让不同子系统之间共享Cookie
- 通过CAS实现SSO系统
- 该方案适用于所有场景
- 基于cookie凭证
实现思路
- 场景需求
- 不同子系统之间是分离的,域名也不一样,比如主系统是
blog.test
,子系统是sub.blog.test
Laravel
添加Cookie
到响应时默认作用域名是当前系统域名
- 不同子系统之间是分离的,域名也不一样,比如主系统是
- 对写入
Cookie
的域名做额外设置,保证主系统和子系统之间可以共享Cookie
凭证 - 子系统和主系统之间共享用户表,子系统中不需要再设置独立的用户表
- 子系统获取用户信息时通过 API 从主系统获取,需要实现 UserProvider 以便获取用户信息
- 关键问题
- Cookie是不能跨域传递的,将一个域的Cookie通知给其它应用(不在同一个域)
解决方案
-
共享Cookie
- Cookie种在父域下,浏览器同域名下的Cookie则可以共享
- 因此可以通过Cookie加解密的算法获取用户SessionID,从而实现SSO
-
问题
- 所有同域名的系统都能获取SessionID,易被修改且不安全
- 跨域无法使用
-
ticket验证
- 用户访问某个子系统,发现如果未登录,则引导用户跳转到SSO登录页面
- 判断SSO是否已经登录
- 如果已经登录,直接跳转到回调地址,并返回认证ticket
- 如果未登录,用户正确输入用户名/密码,认证通过跳转到回调地址,并返回认证ticket
- 子系统获取ticket,调用SSO获取用户uid等信息,成功后让用户登录
-
cookie domain
- Http协议在响应头部,可用Set-Cookie中的domain字段用来表示这个cookie所在的域
- 设置cookie的父域名(domain),客户端访问子域,能够把设置的cookie返回
- 涉及登录凭证(如票据或者用户名)应该加密,cookie不能存放隐私数据
- 通过特殊设置setDomain()可以做到跨同一个大域下的两个子域
焦点
- 高效存储大量临时性的信任数据
- 防止信息传递过程被篡改
- 让SSO系统信任登录系统和免登系统
解决措施
- 采用类似与memcached的分布式缓存的方案,既能提供可扩展数据量的机制,也能提供高效访问
- 采取数字签名的方法,要么通过数字证书签名,要么通过像md5的方式
- 通过白名单来处理,生产信任关系
登录中心Cookie设置
- 单点登录需要一个独立的登录中心,所有系统登录皆从此入,通常将主系统作为登录中心
- 设置Cookie域名
- 设置主系统Cookie作用域名
- 登录成功后,通过
CreateFreshApiToken
中间件将令牌,SessionID写入到Cookie - 在子系统中可读取上述Cookie,并在请求头中带上
流程
-
Cookie
域名通过config/session.php
中的配置项domain
来实现 -
添加 CreateFreshApiToken 中间件(该中间件需要事先安装过 Passport)
-
在 routes/api.php 中新增一条路由,返回认证用户信息
-
设置Session存储媒介
- 基于
Session
实现的单点登录,我们还要让主系统和子系统共享用户Session
信息 - 在登录中心(主系统)安装
predis
扩展composer require predis/predis
- 基于
-
在子系统自定义 UserProvider 实现
- 子系统项目,自定义一个
UserProvider
实现来从主系统获取认证用户信息- 通过
GuzzleHttp
库请求,重写retrieveById
方法
- 通过
- 子系统项目,自定义一个