大家好,我是前端小冯。这是2022年的最后一篇文章,希望对你有帮助。
背景
在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,运营人员每天用自己的账号登录,很方便。但随着企业的发展,用到的系统随之增多,运营人员在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,这对于运营人员来说,很不方便。于是,就想到是不是可以在一个系统登录,其他系统就不用登录了呢?这就是单点登录要解决的问题。
单点登录英文全称Single Sign On,简称就是SSO。是目前比较流行的企业业务整合的解决方案之一。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
如图所示,图中有 4 个系统,分别是 Application1、Application2、Application3、和 SSO。Application1、Application2、Application3 没有登录模块,而 SSO 只有登录模块,没有其他的业务模块,当 Application1、Application2、Application3 需要登录时,将跳到 SSO 系统,SSO 系统完成登录,其他的应用系统也就随之登录了。
单点登录过程
方式一
描述:当单点登录到的子系统都是一家公司的时候使用。
特点:子系统没有自己的登录系统,所有的登录都需要走 SSO
比如 App1 和 App2 都是一家公司的,那么当登录了 App1 后再去进入 App2 的时候,会自动登录。
相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。
间接授权通过令牌(authCode)实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌(authCode),在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌authCode, 然后各个子系统通过单独的单点登录接口,传入 authCode,得到系统登录的 Token,完成登录过程。
这个过程,也就是单点登录的原理,用下图说明:
具体流程如下:
- 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
- sso认证中心发现用户未登录,将用户引导至登录页面
- 用户输入用户名密码提交登录申请
- sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌authCode
- sso认证中心带着令牌跳转会最初的请求地址(系统1)
- 系统1拿到令牌authCode,去sso认证中心校验令牌是否有效
- sso认证中心校验令牌,返回有效,注册系统1
- 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
- 用户访问系统2的受保护资源
- 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
- sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
- 系统2拿到令牌,去sso认证中心校验令牌是否有效
- sso认证中心校验令牌,返回有效,注册系统2
- 系统2使用该令牌创建与用户的局部会话,返回受保护资源
用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话,用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心,全局会话与局部会话有如下约束关系:
- 局部会话存在,全局会话一定存在
- 全局会话存在,局部会话不一定存在
- 全局会话销毁,局部会话必须销毁
方式二
描述:当单点登录到的子系统是不同的公司的时候。
特点:子系统有自己的登录系统,即使没有 SSO 也可以单独登录,SSO 只相当于一个跳板。
如果在子系统登录,走的就是子系统的登录方式,如果想走集中登录,那么登录的入口就是集中登录平台,然后从集中登录平台进入 A 公司的 App1系统时,会携带 authCode,然后在App1系统单独的单点登录接口进行校验 authCode(而不必到 SSO 去校验),然后拿到 Token,完成 App1 的登录。
B 公司的 App2 也是一样的登录过程。
但是如果登录了 App1,再直接跳转到App2 是不能直接进入的,必须从集中登录平台才可以进入。
单点注销过程
方式一
单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明:
下面对上图简要说明
- 用户向系统1发起注销请求
- 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
- sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
- sso认证中心向所有注册系统发起注销请求
- 各注册系统接收sso认证中心的注销请求,销毁局部会话
- sso认证中心引导用户至登录页面
sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作。
方式二
由于方式二中,各个子系统都有自己的登录,注销过程。SSO 登录只是一个跳板而已,所以方式二的注销过程直接调用子系统的注销方式即可,然后window.close()
关闭界面即可。
系统接入单点登录实践
以方式二为例,介绍下一个系统如何集成单端登录功能。
具体流程如下:
- 首先会有一个集中登录平台,需要先登录集中登录平台
- 当集中登录平台登录后,点击子系统会跳转到子系统
- 跳转到子系统的时候会携带 authCode 参数在地址栏上
- 子系统需要在全局路由钩子 router.beforeEach 里面进行判断是否有authCode
- 如果有 authCode,则走集中登录的接口,否则走正常系统登录的逻辑
- 在退出的时候,直接调用系统的登出接口,然后使用
window.close()
关闭界面即可
代码也比较简单:
首先会有一个单点登录的接口,接口的传参可以是一个 authCode,或者有多个参数。
返回值就是我们系统登录完成需要的 Token。
全局路由守卫:
// 导航守卫
router.beforeEach(async (to) => {
try {
//...
// 集中平台登录
const authCode = to.query.authCode;
if (authCode) {
await appStore.ssoLogin(authCode)
return false;
}
//...
} catch (error) {
console.error(error)
}
})
复制代码
登录接口:
登录完成后,设置 Token到本地缓存,设置 SSO 登录的标志(用于退出登录时判断),然后跳转到系统首页即可。
// 集中平台单点登录
async ssoLogin(authCode) {
const token = await apis.portal.ssoLogin({ authCode });
this.afterSsoLogin(token);
},
async afterSsoLogin(token: string) {
this.token = token;
this.isSsoLogin = true;
// 把sso登录状态保存到本地,防止用户刷新界面失效
localMng.setItem(SsoLoginName, true);
localMng.setItem(TokenName, token);
request.setHeader({
Authorization: token,
});
await Promise.all([this.getUserInfo(), this.getMenuList()]).then(()=>{
router.replace("/home");
}).catch(e =>{
ElMessage.error(e);
})
},
复制代码
退出登录:
// 退出登录
const logout = () => {
if(isSsoLogin.value) {
appStore.logout();
localMng.removeItem(SsoLoginName);
window.close();
} else {
appStore.logout();
}
}
复制代码
于是,就完成了系统接入单点登录等前端适配工作,很简单吧。
参考链接:单点登录原理与实现
最后,倘若你读后有新的想法,欢迎留言交流。
你我是朋友,各拿一个苹果彼此交换,交换后仍然是各有一个苹果;倘若你有一个思想,我也有一种思想,而朋友间交流思想,那我们每个人就有两种思想了。 ——萧伯纳