什么是单点登录
前端单点登录(Single Sign On,SSO)是一种用户身份验证和授权的技术,它可以让用户在多个应用或网站之间使用同一个账号和密码进行登录,而无需重复输入或注册。这样可以提高用户体验,降低维护成本,增强安全性。
单点登录方案的简单介绍和实现
前端单点登录的思路主要有以下几种:
基于Cookie的SSO
A页面在认证中心登录成功后,设置Cookie: 这种方式是最常见的SSO实现方式,它的原理是利用浏览器的Cookie机制,在用户第一次登录某个应用时,向认证中心发送请求,认证中心验证用户身份后,返回一个包含用户信息和有效期的加密Cookie,并设置该Cookie的域名为顶级域名(如example.com),这样该Cookie就可以被同一顶级域名下的其他应用(如a.example.com和b.example.com)共享。当用户访问其他应用时,应用会检查浏览器是否携带了有效的Cookie,如果有,则直接登录,如果没有,则跳转到认证中心进行登录。这种方式的优点是简单易实现,缺点是存在跨域问题,只能适用于同一顶级域名下的应用,而且对Cookie的大小和数量有限制。
在A页面在认证中心登录成功后,设置Cookie:
// 生成加密后的Cookie值
const encryptedValue = encrypt(userinfo);
// 设置Cookie
document.cookie = `sso_token=${encryptedValue};domain=.example.com;path=/;max-age=86400;`;
B页面检查是否存在Cookie:
// 获取Cookie
const cookieValue = document.cookie.split(';').find(cookie => cookie.trim().startsWith('sso_token=')).split('=')[1];
// 解密Cookie
const userinfo = decrypt(cookieValue);
// 直接登录
login(userinfo);
基于Token的SSO
这种方式是一种无状态的SSO实现方式,它的原理是在用户第一次登录某个应用时,向认证中心发送请求,认证中心验证用户身份后,返回一个包含用户信息和有效期的加密Token,并存储在浏览器的本地存储(如localStorage或sessionStorage)中。当用户访问其他应用时,应用会检查浏览器是否携带了有效的Token,如果有,则直接登录,如果没有,则跳转到认证中心进行登录。这种方式的优点是可以跨域,不受Cookie的限制,缺点是需要额外的存储空间和网络传输开销,而且存在安全风险,如果Token被盗取或泄露,则可能导致用户身份被冒用。
在A页面登录成功后,将Token存储在localStorage中:
// 生成Token值
const token = generateToken(userinfo);
// 存储Token
localStorage.setItem('sso_token', token);
在其他页面中检查是否存在Token:
// 获取Token
const token = localStorage.getItem('sso_token');
// 验证Token
const userinfo = verifyToken(token);
// 直接登录
login(userinfo);
基于OAuth2.0的SSO
这种方式是一种基于授权码(Authorization Code)的SSO实现方式,它的原理是在用户第一次登录某个应用时,向认证中心发送请求,认证中心验证用户身份后,返回一个授权码,并重定向到应用的回调地址。应用收到授权码后,再向认证中心发送请求,换取一个包含用户信息和有效期的访问令牌(Access Token)和刷新令牌(Refresh Token),并存储在浏览器的本地存储中。当用户访问其他应用时,应用会检查浏览器是否携带了有效的访问令牌,如果有,则直接登录,如果没有,则跳转到认证中心进行登录。这种方式的优点是符合OAuth2.0标准,可以支持多种类型的客户端(如Web、移动、桌面等),缺点是流程较复杂,需要多次请求和重定向。
在A页面中发送授权请求:
const authorizeUrl = `https://auth.example.com/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`;
// 重定向到认证登录页面
window.location.href = authorizeUrl;
在认证页面登录成功后,重定向回A页面的回调地址,并携带授权码:
// 回调地址
const redirectUri = 'https://app.example.com/callback';
// 生成授权码
const code = generateAuthorizationCode(userinfo);
// 重定向回应用的回调地址
window.location.href = `${redirectUri}?code=${code}`;
在A页面中获取访问令牌:
// 获取授权码
const code = getQueryString('code');
// 向认证中心发送获取访问令牌的请求
const tokenUrl = `https://auth.example.com/token?client_id=${clientId}&client_secret=${clientSecret}&code=${code}&grant_type=authorization_code`;
fetch(tokenUrl)
.then(response => response.json())
.then(data => {
// 存储访问令牌和刷新令牌
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
// 直接登录
login(data.userinfo);
});