多端單點登入(SSO)實戰:從架構設計到程式碼實現

多端單點登入

單點登入(SSO)在多端應用中的設計與實現

在現代 Web 應用架構中,用戶往往需要同時使用多個關聯的業務系統(如電商平台的商品頁、購物車、支付中心)。單點登入(SSO)技術透過「一次登入,多系統通行」的特性,徹底解決了用戶在多系統間重複登入的痛點。本文基於簡單的實際專案案例,詳細介紹 SSO 在多端應用中的設計思路與實現方案,包含整頁重定向與彈窗通信兩種核心模式,並結合 client1、client2、client3 的具體程式碼實現進行說明。

一、整體架構設計

這個專案我採用「三端兩模式」架構(多終端協同 + 認證 - 業務分離),透過統一的登入中心串聯多個業務應用,實現跨應用的身份共享。

核心角色劃分

  • 業務應用(Client):client1、client3(基於 Vite + Vue Router 構建的前端應用),提供具體業務功能,需依賴登入狀態訪問受保護資源。兩者程式碼結構基本相同,都透過路由守衛和請求攔截器處理登入相關邏輯。

  • 登入中心(Auth Server):client2(獨立前端應用),負責統一身份認證、token 發放與登入狀態管理,是整個 SSO 體系的信任源。

  • 後端服務(Backend):提供 API 介面的後端服務(運行在 localhost:5000),透過校驗 token 合法性控制資源訪問,當檢測到未授權請求(401 錯誤)時觸發登入流程。

兩種互動模式

SSO 的核心是解決「登入狀態跨應用傳遞」的問題,本專案實現了兩種典型互動模式:

  1. 整頁重定向模式:業務應用透過頁面跳轉將用戶引導至登入中心,登入成功後攜帶 token 重定向回原應用,適用於簡單場景或彈窗被攔截時的降級方案。

  2. 彈窗 + postMessage 模式:業務應用透過 window.open 彈出登入中心,登入成功後利用 postMessage API 將 token 安全傳遞回主應用,保留用戶操作上下文,是本專案的推薦方案。

二、核心登入流程(彈窗 + postMessage 模式)

彈窗模式透過「主應用 - 彈窗 - 主應用」的通信閉環實現登入狀態傳遞,既保證安全性又優化用戶體驗,核心流程分為三個階段:

1. 觸發登入:業務應用檢測未登入狀態

當用戶訪問業務應用的受保護資源時,系統透過兩種方式判斷未登入狀態:

  • 請求攔截:API 請求返回 401 錯誤(後端檢測 token 無效或缺失)。

  • 路由守衛:路由跳轉時,前端路由守衛檢測到本地無有效 token。

此時,業務應用透過 window.open 打開登入中心,並攜帶自身 origin(如 http://localhost:5173)作為 resource 參數,用於登入成功後的回調定位。

以 client1 的響應攔截器程式碼為例:

// client1的request.js響應攔截器
request.interceptors.response.use((res) => {
    if (res.data.status === 401) {
        // 打開登入中心彈窗,攜帶當前應用origin
        window.open(`http://localhost:5174/login?resource=${window.location.origin}`);
    }
    return res;
})

2. 監聽回調:業務應用準備接收 token

業務應用在初始化時註冊 message 事件監聽,專門接收登入中心透過 postMessage 傳遞的 token 資訊。為防止惡意網站偽造消息,需嚴格校驗發送方的 origin。

client1 在 main.js 中註冊監聽:

// client1的main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
window.addEventListener("message",(event) => {
    const token = event.data.token;
    // 存儲接收到的token
    localStorage.setItem("token",token);
    // 刷新頁面使token生效
    window.location.reload()
})
app.use(router).mount('#app')

3. 回傳 token:登入中心完成認證並響應

登入中心加載時解析 resource 參數(業務應用的 origin),用戶完成登入後,透過 postMessage 將 token 定向回傳至業務應用。為保證安全性,需處理 opener 丟失(如彈窗被刷新)的邊緣情況。

client1 中處理登入成功回傳 token 的相關程式碼:

// client1中登入相關邏輯
import request from "../server/request";
import { useRoute } from "vue-router";
import { watch, ref } from "vue";
const route = useRoute();
const resource = ref("");
const token = localStorage.getItem("token");
function postMessage(token, resource){
    window.opener.postMessage({
        token:token
    },resource.value)
}
watch(
  () => route.query.resource,
  (val) => {
    resource.value = val ? decodeURIComponent(val) : "";
    if (token) {
      postMessage(token,resource.value)
    }
  },
  { immediate: true }
);
function login() {
  request.get("/login").then((res) => {
    const apitoken = res.data.data;
    localStorage.setItem("token", apitoken);
    // 向打開登入頁的業務應用回傳token
    window.opener.postMessage({token:apitoken},resource.value)
    // 方案之一:整頁重定向
    window.location.href = `${resource.value}?token=${token}`;
    // 關閉登入彈窗
    window.close()
  });
}

三、路由與令牌接入方案

業務應用需透過路由守衛和請求攔截器實現 token 的自動管理,確保登入狀態在路由跳轉和 API 請求中無縫生效。

1. 路由守衛:控制頁面訪問權限

路由守衛負責在頁面跳轉時校驗登入狀態,對未登入用戶攔截並觸發登入流程,同時處理整頁重定向模式下的 ?token= 參數。

client1 的路由守衛程式碼:

// client1的router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomePage from '../pages/HomePage.vue'
import AboutPage from '../pages/AboutPage.vue'
const routes = [
    { path: '/', component: HomePage },
    { path: '/about', component: AboutPage },
]
const router = createRouter({
    history: createWebHistory(),
    routes,
})
router.beforeEach((to, from, next) => {
    // 處理整頁重定向帶回的token
    const token = to.query.token;
    if (token) {
        localStorage.setItem("token", token);
    }
    next();
})
export default router

2. 請求攔截器:自動攜帶與刷新 token

透過 Axios 攔截器實現 token 的自動攜帶,以及 401 錯誤的統一處理,確保 API 請求的安全性。

client1 的請求攔截器程式碼:

// client1的request.js請求攔截器
request.interceptors.request.use((config) => {
    const token = localStorage.getItem('token')
    config.headers = config.headers || {}
    if (token) config.headers.token = token
    return config
})

四、各端程式碼說明

1. client1 程式碼特點

client1 作為主要的業務應用,實現了完整的 SSO 邏輯:

  • 請求攔截器自動攜帶 token
  • 響應攔截器在 401 時打開登入中心彈窗
  • 路由守衛處理整頁重定向帶來的 token
  • 註冊 message 事件監聽接收登入中心回傳的 token
  • 實現了透過 postMessage 向業務應用回傳 token 的功能

2. client2(登入中心)程式碼特點

client2 作為登入中心,提供登入頁面和登入功能:主要是為了獲取 token

<template>
  <div>
    <div>帳號:<input /></div>
    <div>密碼:<input /></div>
    <div>
      <button
        @click="() => login()"
      >
        登入
      </button>
    </div>
  </div>
</template>

其請求攔截器與 client1 類似,但登入頁一般不做 401 重定向,避免循環跳轉:

// client2的request.js
request.interceptors.response.use((res) => {
    if (res.data.status === 401) {
        window.open(`http://localhost:5174/login?resource=${window.location.origin}`)
    }
    return res;
})

3. client3 程式碼特點

client3 與 client1 程式碼結構基本相同,作為另一個業務應用,驗證 SSO 的跨應用登入效果:

<script setup>
import request from '../server/request'
// 觸發一次請求,用於檢測登入狀態
request.get('/api1').catch(() => {})
</script>
<template>
  <main>
    <h1>Client3 首頁(SSO 演示)</h1>
    <p>如果已在其他專案登入,這裡將直接顯示;否則會被重定向到登入頁。</p>
    <router-link to="/">Home</router-link>
    <router-view></router-view>
  </main>
</template>

五、關鍵細節與避坑指南

單點登入的實現涉及多端互動和安全校驗,以下細節直接影響方案的穩定性和安全性:

  1. postMessage 的 targetOrigin 校驗

必須使用業務應用的 origin(如 http://localhost:5173)作為 targetOrigin,禁止使用 *(允許任意域名接收),否則可能導致 token 被惡意網站竊取。

  1. window.opener 的可用性處理

僅當登入中心透過 window.open 打開時,opener 才指向業務應用窗口;若用戶刷新登入彈窗,opener 會變為 null,需降級為整頁重定向,如 client1 的 login 函數中同時實現了 postMessage 和整頁重定向。

  1. resource 參數的編解碼

業務應用傳遞 origin 時需用 encodeURIComponent 編碼(處理特殊字符),登入中心接收後用 decodeURIComponent 解碼,避免 URL 解析錯誤,如 client1 中 watch 監聽 resource 參數時進行了解碼處理。

  1. 路由守衛必須調用 next()

無論是否允許路由跳轉,beforeEach 守衛都必須調用 next(),否則會導致頁面卡死(不渲染),client1 的路由守衛中正確實現了這一點。

  1. 彈窗被瀏覽器攔截的兼容

若瀏覽器因「非用戶主動觸發」攔截彈窗(如自動執行的 window.open),需捕獲錯誤並降級為整頁重定向。

六、兩種模式的選用場景

模式 優勢 劣勢 适用场景
彈窗 + postMessage 不刷新頁面,保留用戶操作上下文;體驗流暢 需處理彈窗攔截、opener 丟失等邊緣情況 主流場景:用戶主動觸發的登入(如點擊「我的」按鈕)
整頁重定向 實現簡單,無瀏覽器兼容性問題 刷新頁面,丟失當前操作狀態 降級方案:彈窗被攔截時;簡單應用或舊瀏覽器

推薦實現「智能兼容」邏輯:登入中心優先嘗試 postMessage 回傳,檢測到 opener 無效時自動切換為整頁重定向,確保所有場景下的可用性,client1 的登入函數已實現此邏輯。

七、總結

單點登入(SSO)的核心是透過「統一認證 + 跨域通信」實現多應用的身份共享。本專案透過「彈窗 + postMessage」模式優化用戶體驗,同時以整頁重定向作為兜底,兼顧了安全性與兼容性。

關鍵成功要素包括:

  • 嚴格的 origin 校驗,防止 token 跨域洩露;
  • 完善的邊緣情況處理(如 opener 丟失、彈窗攔截);
  • 統一的 token 管理機制(路由守衛 + 請求攔截器)。

透過這套示例,業務應用(client1、client3)無需關心登入邏輯,只需專注自身業務;登入中心(client2)統一處理身份認證,實現「一次登入,全平台通行」的目標,為用戶提供無縫的跨應用體驗。實際應用中,可根據具體業務場景對這套程式碼進行擴展和優化,如增加 token 過期刷新機制、完善錯誤處理等。


如果您覺得這篇文章對您有幫助,歡迎點讚和收藏,大家的支持是我繼續創作優質內容的動力🌹🌹🌹也希望您能在😉😉😉我的首頁 😉😉😉找到更多對您有幫助的內容。

  • 致敬每一位趕路人

原文出處:https://juejin.cn/post/7546053362467389449


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝8   💬8   ❤️16
409
🥈
我愛JS
📝1   💬6   ❤️4
90
🥉
酷豪
📝1   ❤️1
49
#4
AppleLily
📝1   💬4   ❤️1
37
#5
💬3  
10
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次