效率与安全的双重革命
研究表明,普通员工每年因重复登录浪费超44小时,而SSO的实施可减少63%的IT支持工单,同时通过集中权限管理将密码泄露风险降低87%。对前端而言,SSO不仅是跳转逻辑的实现,更是安全路由的设计艺术——需精准控制认证跳转、安全传递授权码、规范存储令牌。
前端核心职责边界
认证跳转控制:引导用户至认证中心,携带PKCE参数
授权码传递:安全捕获并转发认证中心下发的code
令牌存储管理:根据安全策略选择存储介质
敏感操作隔离:令牌兑换等操作必须由后端完成,严防密钥泄露
前端行为
应用初始化时检查本地是否存在有效令牌(如sessionStorage
中的access_token
)。若缺失则重定向至认证中心,并携带PKCE参数增强安全性。
关键代码
// 生成PKCE参数
const codeVerifier = generateRandomString();
const codeChallenge = base64UrlEncode(sha256(codeVerifier));
// 构建认证请求
const ssoUrl = `https://sso-server.com/auth?client_id=APP_A&code_challenge=${codeChallenge}`;
window.location.href = ssoUrl;
Code解析
认证中心回调时,前端需从URL参数提取授权码:
const urlParams = new URLSearchParams(window.location.search);
const authCode = urlParams.get('code');
后端协作
前端将authCode
发送给自身后端(非认证中心),由后端完成令牌兑换:
fetch('/api/exchange-token', {
method: 'POST',
body: JSON.stringify({ code: authCode, code_verifier }) // 携带PKCE验证参数
});
Cookie共享机制
通过设置Domain=.example.com
实现父子域名共享,需配合属性:
Set-Cookie: token=xxx; Domain=.example.com; Path=/; SameSite=None; Secure
局限:无法跨主域工作,且需防范CSRF攻击(推荐添加__Host-
前缀)
iframe + postMessage
在认证中心部署sync.html
,子系统通过隐藏iframe同步令牌:
// 子系统监听消息
window.addEventListener('message', (event) => {
if (event.origin === 'https://sso-server.com') {
storeToken(event.data.token);
}
});
关键安全点:严格校验event.origin
,避免恶意域名窃取数据
降级兼容方案
老旧系统可使用JSONP拉取状态(如ssoCallback({token: 'xyz'})
),但需注意仅支持GET请求且易受XSS攻击
信令同步模式
主动广播:认证中心调用预注册的系统退出接口
被动轮询:子系统定期向认证中心校验令牌状态
前端清理动作
// 清除存储
sessionStorage.removeItem('access_token');
// 跨域Cookie删除
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=.example.com; path=/';
URL参数化令牌传递
允许通过加密的一次性令牌初始化登录:
const token = new URLSearchParams(location.search).get('sso_token');
if (token) exchangeToken(token); // 提交后端验证
安全要求:Token需设置短有效期(建议<60秒)并加密
Service Worker拦截请求实现无感知认证:
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
const token = sessionStorage.getItem('token');
if (token) {
event.request.headers.append('Authorization', `Bearer ${token}`);
}
}
});
边界限制:仅限同域使用,无法解决跨域问题
// store.js
const store = new Vuex.Store({
state: { token: null },
mutations: {
setToken(state, token) { state.token = token }
},
actions: {
async ssoLogin({ commit }) {
const { code } = await authing.getAuthorizationCode();
const { token } = await api.exchangeCode(code); // 后端兑换
commit('setToken', token);
}
}
})
// router.js
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.token) {
next({ path: '/sso-redirect' }); // 触发SSO流程
} else next();
});
回答逻辑框架
概念先行:简述SSO解决的核心问题(分布式系统身份同步)
流程拆解:明确划分四个阶段
→ 跳转认证中心 → 获取Code → 后端换Token → 存储使用
安全强调:
“前端不处理敏感操作(如Code换Token)”
“必须采用PKCE防止授权码截持”
“跨域方案需严格校验postMessage
的origin”
技术对比:
“隐式模式(Implicit)因令牌暴露已被OAuth 2.1废弃,现代SSO首选授权码+PKCE模式”
高频考题应对
Token失效怎么办?
“在请求拦截器中捕获401错误,调用刷新接口(需提前存refresh_token
至HttpOnly Cookie),若刷新失败则重定向至SSO中心”
如何实现全系统退出?
“子系统退出时调用认证中心注销接口,认证中心广播指令至所有注册系统,各系统清除本地会话并删除存储”
单点登录在前端的落地,本质是安全、体验与兼容性的三角平衡。同域场景优选Cookie共享,跨域方案则需在iframe+postMessage
的健壮性与JSONP的兼容性间取舍。无论方案如何演进,前端需始终恪守安全边界:不持密钥、不传敏感、不越权处理认证逻辑。
当浏览器逐步封锁第三方Cookie,新一代无Cookie SSO方案(如OAuth 2.0 Device Flow)已崭露头角。技术会变,但核心原则不变:前端是安全链条的守护者,而非认证逻辑的执行者。
写在最后