Vue.js 单点登录(SSO)实现完全指南

一、什么是单点登录(SSO)

单点登录(Single Sign-On,简称SSO)是一种身份验证机制,用户只需在一个系统中登录一次,即可访问所有相互信任的关联系统,而无需重复认证。例如,登录Google账号后可以直接使用Gmail、YouTube、Google Drive等服务。

SSO的核心流程可以概括为:

  1. 用户访问子系统A,被重定向至中央认证中心
  2. 用户在认证中心登录后,认证中心颁发令牌(Token)
  3. 子系统A通过验证令牌获取用户信息
  4. 用户访问子系统B时,重复验证令牌流程,无需再次登录

对于前端Vue.js应用而言,实现SSO的核心职责包括:检测登录状态、处理认证跳转、管理Token令牌、以及跨应用同步登录状态。

二、SSO的主流实现方式对比

根据2024-2026年的前端实践,SSO主要有三种落地方式:

方式一:基于Cookie的域共享(传统方式)

适用场景:所有应用在同一顶级域下(如 app1.company.comapp2.company.comsso.company.com)。

核心机制 :SSO服务器设置Cookie时指定 domain=.company.com; Secure; HttpOnly; SameSite=Lax,浏览器在所有子域自动携带该Cookie。

优缺点:浏览器原生支持,前端几乎无感实现;但仅限子域场景,无法跨顶级域。企业内网、传统ToB系统仍大量使用,但新项目已逐步转向Token模式。

方式二:基于Token的集中式认证(当前最推荐)

适用场景:跨顶级域、多前端框架(React/Vue/Next.js)、移动端与Web混合。

核心流程(OIDC + Authorization Code + PKCE + Refresh Token):

  1. 用户访问任意前端应用 → 未登录 → 重定向到SSO中心
  2. SSO中心登录成功 → 返回授权码(code)
  3. 前端用PKCE换取Token(access_token + refresh_token)
  4. 前端存储Token并携带在后续请求的Header中

优点:跨域支持好,不依赖第三方Cookie,安全性高,是当前主流方案。

方式三:iframe + postMessage通信(过渡方案)

适用场景:遗留系统集成、临时桥接。

核心机制 :业务应用通过 window.open 弹出登录中心,登录成功后利用 postMessage API 将Token安全传递回主应用。

局限性:受浏览器隐私策略限制(第三方Cookie逐步淘汰),已不再是推荐方案。

三、Vue.js前端SSO实现的完整步骤

1. 系统架构设计

一个典型的SSO系统包含三个核心角色:

  • 认证中心(Auth Server) :独立的前端应用或服务,负责统一身份认证、Token发放与登录状态管理
  • 业务应用(Client) :Vue.js构建的前端应用,提供具体业务功能,依赖登录状态访问受保护资源
  • 后端服务(Backend) :提供API接口,通过校验Token合法性控制资源访问

在技术选型上,常见组合为:Vue(前端) + JWT(令牌) + OAuth2.0/OIDC(协议)

2. 配置路由守卫

路由守卫是SSO实现的第一道防线,用于拦截未登录用户的访问请求。

Vue Router全局前置守卫示例

javascript 复制代码
// router.js
import router from './router';
import store from './store';

router.beforeEach((to, from, next) => {
  const isAuthenticated = store.getters['auth/isLoggedIn'];
  // 如果路由需要认证且用户未登录,跳转到登录页
  if (to.meta.requiresAuth && !isAuthenticated) {
    next({ 
      path: '/login', 
      query: { redirect: to.fullPath } 
    });
  } else {
    next();
  }
});

更完整的SSO路由守卫逻辑

javascript 复制代码
router.beforeEach(async (to, from, next) => {
  let userInfo = storageLocal().getItem(userKey); // 检查本地缓存
  if (!userInfo) {
    let currentUrl = window.location.href;
    
    // 1. 检测是否为SSO回调(URL中带有授权码)
    const match = currentUrl.match(/code=([^&#]*)/);
    const code = match ? match[1] : null;
    
    if (code) {
      // 2. 有授权码 → 用code换取token和用户信息
      await loginByCode(code);
    } else if (to.query.type === 'sso') {
      // 3. 需要SSO认证 → 跳转到认证中心
      currentUrl = currentUrl.replace('sso', 'sse'); // 防止死循环
      const ssoUrl = await getSsoUri(currentUrl);
      window.location.href = ssoUrl;
    }
  }
  // 再次检查用户信息,决定跳转目标
  userInfo = storageLocal().getItem(userKey);
  if (!userInfo) {
    // 跳转到登录页
  }
  next();
});

关键要点

  • 使用 to.meta.requiresAuth 标记需要认证的路由
  • 检测URL中的授权码(code)参数,完成认证回调处理
  • 通过 query 参数(如 type=sso )触发SSO跳转流程
  • 注意防止死循环跳转(如将 sso 替换为 sse

3. 登录跳转与Token处理

当检测到用户未登录时,前端需要将用户重定向到认证中心。

登录跳转实现

javascript 复制代码
// Login.vue
export default {
  methods: {
    redirectToAuthServer() {
      const authUrl = `${authServerUrl}/login?client_id=${clientId}&redirect_uri=${encodeURIComponent(
        window.location.origin + '/callback'
      )}`;
      window.location.href = authUrl;
    },
    // 回调页面解析Token
    handleCallback() {
      const token = this.$route.query.token;
      if (token) {
        this.$store.dispatch('auth/saveToken', token);
        this.$router.replace(this.$route.query.redirect || '/');
      }
    }
  }
}

处理认证回调的关键步骤

  1. 前端通过重定向将用户引导至认证中心,携带 redirect_uri 参数
  2. 认证中心登录成功后,携带授权码(code)或Token重定向回前端
  3. 前端在回调页面解析URL参数,获取Token并存储

优化技巧 :将登录流程完全置于Vue Router的控制之下,利用导航守卫和编程式导航来管理状态,避免硬刷新带来的体验问题。当检测到code时,可以在守卫中挂起导航(返回pending Promise),待Token获取完成后再通过 router.replace() 完成无痕路由替换。

4. Token存储与管理

Token的存储和管理是SSO实现的核心环节。

使用Vuex/Pinia进行状态管理

javascript 复制代码
// store/auth.js
const state = {
  token: null
};

const mutations = {
  SET_TOKEN(state, token) {
    state.token = token;
    localStorage.setItem('jwt', token); // 持久化存储
  },
  CLEAR_TOKEN(state) {
    state.token = null;
    localStorage.removeItem('jwt');
  }
};

const actions = {
  saveToken({ commit }, token) {
    commit('SET_TOKEN', token);
  },
  logout({ commit }) {
    commit('CLEAR_TOKEN');
    // 跳转到登录页
    window.location.href = '/login';
  }
};

Token存储的安全建议

  • Access Token:建议存储在内存中(Pinia/Vuex状态),配合XSS防护
  • Refresh Token:建议存储在HttpOnly Cookie中,防止XSS攻击窃取
  • 本地存储(localStorage) :方便页面刷新后恢复状态,但需注意XSS风险

在Vue 3项目中,推荐使用 Pinia 进行Token状态管理,它更简洁、类型支持更好。

5. 全局Axios拦截器

Axios拦截器负责在每次请求中自动携带Token,并处理Token过期时的自动刷新。

请求拦截器:自动在请求头中添加Token

javascript 复制代码
// axios.js
import axios from 'axios';
import store from './store';

axios.interceptors.request.use(config => {
  const token = store.getters['auth/token'];
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

响应拦截器:处理401未授权错误,触发重新登录

javascript 复制代码
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response.status === 401) {
      store.dispatch('auth/logout');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

Token无感刷新机制

  • 在请求拦截器中检查Token是否即将过期,若是则先发起静默刷新请求
  • 在响应拦截器中检测401状态,自动刷新Token并重试原请求
  • 多个请求同时失败时,使用请求队列避免重复刷新

四、弹窗 + postMessage 模式(可选方案)

除了整页重定向,弹窗模式是另一种常见的SSO交互方式,用户体验更佳。

核心流程

1. 触发登录 :业务应用检测到未登录状态后,通过 window.open 打开登录中心弹窗

javascript 复制代码
// 响应拦截器中触发登录弹窗
request.interceptors.response.use((res) => {
  if (res.data.status === 401) {
    window.open(`http://sso-server.com/login?resource=${window.location.origin}`);
  }
  return res;
});

2. 监听回调 :业务应用注册 message 事件监听,接收登录中心传递的Token

javascript 复制代码
// main.js 中注册监听
window.addEventListener('message', (event) => {
  // 严格校验发送方的origin,防止恶意伪造
  if (event.origin !== 'http://sso-server.com') return;
  const { token } = event.data;
  if (token) {
    store.dispatch('auth/saveToken', token);
  }
});

3. 安全校验 :必须严格校验 event.origin,防止恶意网站伪造消息

五、常见SSO协议的前端对接

OAuth2.0 / OIDC 协议

OAuth2.0 + OpenID Connect(OIDC)是目前最主流的SSO协议方案。前端主要工作包括:

  1. 选择合适的SSO协议和服务
  2. 在Vue项目中配置SSO相关的库和插件
  3. 实现授权码流程(Authorization Code Flow)
  4. 处理认证后的用户信息

社区方案 :可使用 @ztzx/vue-sso-interceptor 等Vue 3 SSO插件,它提供了完整的OAuth2/OIDC认证流程支持,包括自动路由守卫、Token管理、Pinia状态集成等功能。

CAS协议

CAS(Central Authentication Service)是另一种开源的单点登录协议。在Vue项目中集成CAS的要点:

  1. 配置CAS服务器地址
  2. 在路由守卫中判断无Token时跳转到CAS认证中心(替换原有的登录页跳转逻辑)
  3. 处理CAS回调,获取Ticket并换取用户信息

核心改造思路:将CAS认证流程从"后端驱动、浏览器自动跳转"转变为"前端感知、主动协作"的API调用模式。

六、安全最佳实践

1. Token安全存储

  • Access Token避免存储在localStorage(易受XSS攻击)
  • Refresh Token使用HttpOnly Cookie存储
  • 考虑使用内存存储 + 定期刷新的组合方案

2. 防止CSRF攻击

  • 使用SameSite Cookie属性
  • 在请求中添加CSRF Token
  • 使用PKCE(Proof Key for Code Exchange)增强授权码流程安全性

3. 跨域安全

  • 严格校验postMessage的 event.origin
  • 合理配置CORS策略
  • 避免在前端暴露敏感信息(如client_secret)

4. 登出处理

  • 清除前端本地存储的Token
  • 调用后端登出接口
  • 跳转到SSO中心的全局登出页面,实现"一处登出,处处登出"

七、2026年趋势与挑战

随着浏览器隐私策略的收紧(第三方Cookie逐步被禁用),SSO前端实现正经历重要变革:

  • 第三方Cookie基本被禁用:Chrome已100%推进,传统依赖Cookie的SSO方案受到冲击
  • Storage Partitioning:不同顶级域的localStorage相互隔离
  • 演进方向:从"纯Cookie共享" → "纯Token集中式" → "混合/BFF模式"成为主流路径

对于Vue前端开发者而言,基于Token的集中式认证(OIDC + PKCE + Refresh Token) 是当前最推荐的SSO实现方案,具有良好的跨域支持和浏览器兼容性。

八、总结

在Vue.js中实现单点登录,前端主要承担以下职责:

  1. 配置身份验证服务:选择合适的SSO协议(OAuth2/OIDC/CAS)和认证服务
  2. 配置路由守卫:保护需要认证的路由,拦截未登录访问
  3. 处理认证回调:解析授权码,换取并存储Token
  4. 实现全局状态管理:使用Vuex或Pinia集中管理用户登录状态
  5. 配置请求拦截器:自动携带Token,处理Token过期刷新

通过以上步骤,Vue前端应用可以完整实现"一次登录,多系统通行"的SSO能力,在提升用户体验的同时保障系统安全。