一、單點登錄(SSO)是什么?
核心定義
單點登錄(Single Sign-On,SSO)是一種身份認證解決方案,允許用戶通過一次登錄訪問多個相互信任的應用系統。其核心邏輯是統一認證中心與分布式會話管理,避免用戶重復輸入憑證,提升跨系統訪問效率。
技術原理
- 獨立認證中心(Passport)
所有子系統的登錄請求均由認證中心處理,子系統本身不存儲用戶憑證。認證中心負責驗證用戶身份,并頒發全局唯一的令牌(Token)或會話標識(Session ID)。 - 令牌傳遞與會話共享
- 認證中心登錄成功后,生成令牌并通過安全方式(如 Cookie、Header)傳遞給子系統。
- 子系統通過令牌向認證中心驗證用戶身份,驗證通過后建立局部會話(存儲用戶權限等信息),實現免密訪問。
- 跨系統會話一致性
- 全局會話:用戶與認證中心的會話,標識用戶整體登錄狀態。
- 局部會話:用戶與單個子系統的會話,依賴全局會話存在。
- 約束關系:全局會話銷毀時,所有局部會話需同步銷毀;局部會話存在時,全局會話必然存在。
經典案例:電商平臺跨應用登錄
以 React 電商中臺系統 為例:
-
主站(
main.com
)使用 React 開發,管理后臺(admin.com
)使用 Vue3 開發,兩者域名不同。 -
用戶在主站登錄后,認證中心(
sso.com
)生成 Token,并通過以下方式實現跨域共享:
- 主站將 Token 存儲于 LocalStorage,并通過
postMessage
傳遞給管理后臺的 iframe 頁面,寫入其 LocalStorage。 - 管理后臺前端每次請求時攜帶 Token,后端驗證通過后建立局部會話,用戶無需重復登錄。
- 主站將 Token 存儲于 LocalStorage,并通過
二、如何實現單點登錄?
場景一:同域名下的單點登錄(子域共享 Cookie)
適用場景:同一主域名下的子系統(如 shop.xxx.com
和 admin.xxx.com
)。
實現原理:利用 Cookie 的 domain 和 path 屬性跨子域共享會話標識。
步驟(以 Vue3 為例):
-
設置認證中心 Cookie
在認證中心登錄成功后,將 Session ID 寫入 Cookie,配置:document.cookie = `sessionId=xxx; domain=.xxx.com; path=/; secure; HttpOnly`;
domain=.xxx.com
:允許所有子域訪問該 Cookie。path=/
:根路徑下所有資源均可讀取。
-
子系統驗證會話
子系統(如 Vue3 應用)在路由守衛中檢查 Cookie 中的sessionId
:// Vue3 路由守衛 router.beforeEach((to, from, next) => {const sessionId = document.cookie.match(/sessionId=([^;]+)/)?.[1];if (to.meta.requiresAuth && !sessionId) {next(`/sso/login?redirect=${encodeURIComponent(to.fullPath)}`); // 跳轉認證中心} else {next();} });
優勢:實現簡單,無需后端復雜邏輯;局限:僅適用于同主域名場景。
場景二:不同域名下的單點登錄(標準 SSO 方案)
適用場景:跨域名系統(如 react-app.com
與 vue-app.com
)。
核心方案:基于 認證中心 + 令牌校驗 的分布式架構。
方案一:后端主導的重定向認證(通用方案)
技術棧:認證中心(Node.js/Java) + 子系統(React/Vue3)。
流程步驟:
-
用戶訪問子系統(React 應用)
子系統檢測到未登錄,重定向至認證中心,并攜帶回調地址:// React 前端重定向 window.location.href = `https://sso.com/login?redirect=https://react-app.com/dashboard`;
-
認證中心處理登錄
-
用戶輸入憑證,認證中心驗證通過后生成 JWT 令牌,并存儲用戶會話(如 Redis)。
-
重定向回子系統,附帶令牌參數:
// 認證中心 Node.js 后端 app.get('/login', (req, res) => {// 驗證用戶邏輯...const token = generateJWT(user.id);res.redirect(`${req.query.redirect}?token=${token}`); });
-
-
子系統校驗令牌
子系統(Vue3 應用)后端接收令牌,調用認證中心接口驗證合法性:// Vue3 后端(Node.js) app.get('/dashboard', async (req, res) => {const token = req.query.token;const isValid = await fetch(`https://sso.com/verify-token?token=${token}`); // 校驗令牌if (isValid) {// 創建局部會話(如設置子系統 Cookie)res.cookie('localSession', 'valid', { domain: 'vue-app.com' });res.send('受保護資源');} else {res.redirect('/login');} });
優勢:安全性高,支持跨域;劣勢:需要后端深度參與,流程較復雜。
方案二:前端主導的 LocalStorage 共享(輕量級方案)
技術棧:純前端(React + Vue3) + 認證中心 API。
核心邏輯:通過 iframe
+ postMessage
跨域傳遞 Token 至各系統的 LocalStorage。
實現步驟(以 React 為例):
-
認證中心返回 Token 給主系統
// React 主系統登錄回調 const handleLogin = async () => {const response = await fetch('https://sso.com/login', { method: 'POST', body: formData });const { token } = await response.json();// 存儲主系統 TokenlocalStorage.setItem('mainToken', token);// 通過 iframe 向 Vue3 子系統傳遞 Tokenconst iframe = document.createElement('iframe');iframe.src = 'https://vue-app.com/sso-helper'; // Vue3 子系統的跨域接收頁面document.body.appendChild(iframe);setTimeout(() => {iframe.contentWindow.postMessage(token, 'https://vue-app.com'); // 安全限制目標域名}, 1000); };
-
Vue3 子系統接收 Token
創建sso-helper.html
頁面(Vue3 項目靜態文件),監聽跨域消息并存儲 Token:<!-- Vue3 子系統 sso-helper.html --> <script>window.addEventListener('message', (event) => {if (event.origin === 'https://react-app.com') { // 驗證來源域名localStorage.setItem('vueToken', event.data);}}); </script>
-
各系統請求攜帶 Token
前端每次發請求時從 LocalStorage 讀取 Token 并添加到請求頭:// React 全局 Axios 攔截器 axios.interceptors.request.use(config => {config.headers.Authorization = `Bearer ${localStorage.getItem('mainToken')}`;return config; });
優勢:后端無侵入,部署靈活;風險:依賴前端安全性,需嚴格校驗消息來源域名。
三、單點登錄核心流程與代碼示例
流程時序圖
關鍵代碼片段(跨域場景)
-
認證中心生成 Token(Node.js)
const jwt = require('jsonwebtoken'); app.post('/login', (req, res) => {const { username, password } = req.body;// 驗證用戶邏輯...const token = jwt.sign({ userId: '123' }, 'sso-secret-key', { expiresIn: '1h' });res.json({ token }); // 返回給前端,由前端處理跨域傳遞 });
-
React 前端跨域傳遞 Token 至 Vue3
// React 登錄成功后 const sendTokenToVueApp = (token) => {const iframe = document.createElement('iframe');iframe.style.display = 'none';iframe.src = 'https://vue-app.com/sso-iframe'; // Vue3 接收頁面document.body.appendChild(iframe);iframe.onload = () => {iframe.contentWindow.postMessage({ type: 'SSO_TOKEN', token },'https://vue-app.com');}; };
-
Vue3 前端監聽跨域消息
// Vue3 主文件 main.js mounted() {window.addEventListener('message', this.handleSSOMessage); }, methods: {handleSSOMessage(event) {if (event.origin === 'https://react-app.com' && event.data.type === 'SSO_TOKEN') {localStorage.setItem('ssoToken', event.data.token);this.$router.push('/dashboard'); // 自動跳轉已登錄頁面}} }
四、常見問題與解決方案
- 跨域 Cookie 共享限制
- 同域名場景通過
domain
屬性解決,不同域名需依賴認證中心重定向或前端跨域通信(如postMessage
)。
- 同域名場景通過
- 令牌安全性
- 使用 HTTPS 傳輸 Token,避免明文泄露;
- 令牌設置短有效期(如 1 小時),搭配刷新令牌(Refresh Token)機制續期。
- 單點登出(SLO)
- 全局登出時,認證中心需銷毀全局會話,并通知所有子系統銷毀局部會話(可通過廣播消息或子系統主動校驗令牌失效)。
- 跨域 Cookie 共享限制
五、技術選型總結
場景 | 推薦方案 | 技術棧示例 | 優勢 |
---|---|---|---|
同域名子系統 | Cookie 共享 | React + Express | 簡單高效,后端輕量級 |
跨域名企業系統 | 認證中心 + 重定向校驗 | Vue3 + Spring Boot + Redis | 安全性高,符合企業級規范 |
純前端跨域應用 | LocalStorage + postMessage | React + Vue3 + Node.js | 前端主導,后端無侵入 |