现代Web认证体系深度解析:从JWT原理到SSO架构设计

一、引言:SSO的核心价值与实现挑战

多系统登录痛点的业务场景分析

在现代企业数字化环境中,员工每天需要访问数十个不同的业务系统:OA办公系统、CRM客户关系管理、ERP企业资源计划、HR人力资源系统、财务报销系统等。每个系统都要求独立的用户名和密码登录,这种"系统孤岛"现象带来了严重的用户体验和工作效率问题。

真实业务场景案例:

  • 某大型电商公司:运营人员需要同时操作商品管理系统、订单处理系统、客服工单系统、数据报表系统。每天重复登录4-5次,每次输入不同的账号密码。

  • 金融机构:客户经理使用信贷审批系统、客户信息管理系统、风险控制系统时,需要在不同系统间频繁切换,登录过程繁琐。

  • 互联网企业:开发者需要访问代码仓库、持续集成平台、监控系统、文档管理系统,多个独立的认证体系增加了时间成本。

数据表明:根据用户体验研究,用户平均需要记住7个不同的系统密码,每月因密码遗忘和重置浪费约30分钟的工作时间。更严重的是,为了记忆方便,62%的用户在不同系统使用相同或相似的密码,这大大增加了安全风险。

SSO要解决的核心问题:一次登录,全网通行

单点登录(Single Sign-On,SSO)的核心价值在于解决"认证碎片化"问题,实现统一的身份治理。其要解决的核心问题包括:

  1. 用户体验优化:消除重复登录,实现无缝系统切换
  2. 安全管控统一:集中管理认证策略,避免弱密码扩散
  3. 运维效率提升:统一账号生命周期管理,降低维护成本
  4. 合规审计强化:集中记录认证日志,满足安全审计要求

理想效果:用户只需在一天开始时登录一次,即可访问所有授权系统,就像拥有了一张"数字通行证"在各个办公大楼间自由通行。

不同技术路线的选择困境:Cookie vs Token

在SSO技术选型时,架构师面临的核心困境:选择传统的Cookie-Session方案还是现代的Token-Based方案?

Cookie方案的诱惑

  • 浏览器原生支持,实现相对简单
  • 会话管理成熟,有大量成功案例
  • 对于传统Web应用兼容性好

Token方案的吸引力

  • 更适合前后端分离架构
  • 天然支持移动端和跨域场景
  • 无状态设计便于水平扩展

技术选型决策矩阵

评估维度 ✅ Cookie方案优势 ✅ Token方案优势 🔍 当前企业情况
系统架构 单体应用/传统Web 微服务/前后端分离 ✅ 计划微服务架构
多端支持 主要支持Web浏览器 支持Web/App/小程序 ✅ 需要移动端App
跨域需求 同域或父子域名 支持任意跨域 ✅ 存在跨域需求
团队技术栈 Session管理经验丰富 JWT/OAuth经验丰富 ⚠️ JWT经验有限
扩展性 需要会话集群 无状态易扩展 ✅ 希望无状态扩展
安全要求 需防CSRF攻击 需防Token泄露 🔶 两者都需要

混合方案的现实选择

很多企业采用渐进式迁移策略,在保持现有Cookie-Session体系的同时,为新系统引入Token认证,通过网关进行协议转换,实现平滑过渡。

timeline title 推荐实施路径:渐进式迁移 section 短期 (1-3个月) Cookie方案维持现状 : 现有系统保持稳定 Token技术培训 : 团队技能提升 section 中期 (3-6个月) 混合方案过渡 : 网关层协议转换 新项目采用Token : 移动端先行 section 长期 (6-12个月) 全面转向Token : 统一认证体系 老系统逐步改造 : 按优先级迁移

二、SSO登录态的核心承载机制

2.1 Cookie机制:传统Web的基石

Cookie的技术原理与结构解析

Cookie是网站在用户浏览器中存储的小型文本数据,其核心技术原理是客户端状态存储自动请求携带

Cookie的完整结构

http 复制代码
Set-Cookie: sessionId=a1b2c3d4e5f6; 
Domain=.company.com; 
Path=/; 
Expires=Wed, 21 Oct 2025 07:28:00 GMT; 
Max-Age=3600; 
Secure; 
HttpOnly; 
SameSite=Lax

各字段深度解析

  • Name=Value:核心数据,如会话标识
  • Domain:指定哪些域名可以访问该Cookie
  • Path:控制Cookie的URL路径范围
  • Expires/Max-Age:设置Cookie的生命周期
  • Secure:仅通过HTTPS传输
  • HttpOnly:阻止JavaScript访问,防XSS
  • SameSite:控制跨站请求时是否发送Cookie

SessionID的工作机制与服务端存储

Cookie-Session机制的核心是服务端会话状态管理

javascript 复制代码
// 服务端Session存储结构
class SessionStore {
  constructor() {
    this.sessions = new Map(); // 实际使用Redis等分布式存储
  }
  
  createSession(userId) {
    const sessionId = this.generateSessionId();
    const sessionData = {
      userId: userId,
      loginTime: Date.now(),
      lastAccess: Date.now(),
      userAgent: this.getUserAgent(),
      ipAddress: this.getClientIP(),
      // 其他会话上下文信息
    };
    
    this.sessions.set(sessionId, sessionData);
    return sessionId;
  }
  
  validateSession(sessionId) {
    const session = this.sessions.get(sessionId);
    if (!session) return false;
    
    // 检查会话是否过期
    if (Date.now() - session.lastAccess > this.sessionTimeout) {
      this.sessions.delete(sessionId);
      return false;
    }
    
    // 更新最后访问时间
    session.lastAccess = Date.now();
    return session;
  }
  
  destroySession(sessionId) {
    this.sessions.delete(sessionId);
  }
}

会话生命周期管理

  1. 创建:用户登录成功后生成唯一SessionID
  2. 存储:SessionID存入Cookie,会话数据存入服务端存储
  3. 验证:每次请求通过SessionID查找并验证会话
  4. 销毁:用户退出或超时后清理会话数据

域(domain)与路径(path)的隔离规则

Cookie的作用域控制是SSO设计的关键:

Domain规则示例

javascript 复制代码
// 父子域名Cookie共享
const domainScenarios = {
  exactDomain: {
    set: "Set-Cookie: session=abc; Domain=app.company.com",
    access: "仅在app.company.com下发送"
  },
  parentDomain: {
    set: "Set-Cookie: session=abc; Domain=.company.com", 
    access: "在company.com所有子域名下发送:app.company.com、api.company.com、www.company.com"
  },
  crossDomain: {
    limitation: "无法在不同主域名间共享Cookie:company.com与partner.com"
  }
};

Path规则示例

javascript 复制代码
const pathScenarios = {
  rootPath: {
    set: "Set-Cookie: session=abc; Path=/",
    access: "整个域名下的所有路径都可访问"
  },
  specificPath: {
    set: "Set-Cookie: adminSession=xyz; Path=/admin",
    access: "仅在/admin及子路径如/admin/users下发送"
  }
};

安全属性:HttpOnly、Secure、SameSite

现代Cookie安全的三重防护:

HttpOnly - 防御XSS攻击

javascript 复制代码
// 不安全的Cookie - 可通过XSS窃取
document.cookie = "session=abc123"; 

// 安全的HttpOnly Cookie - JavaScript无法访问
// Set-Cookie: session=abc123; HttpOnly

// 攻击者尝试窃取时
try {
  const stolenSession = document.cookie; // 无法获取HttpOnly Cookie
  sendToAttacker(stolenSession); // 攻击失败
} catch (e) {
  console.log("HttpOnly Cookie保护生效");
}

Secure - 强制HTTPS传输

javascript 复制代码
// 生产环境强制HTTPS + Secure Cookie
if (location.protocol === 'https:') {
  // Set-Cookie: session=abc; Secure
  // 仅在HTTPS下传输,防止中间人攻击
}

SameSite - 防御CSRF攻击

javascript 复制代码
const sameSiteStrategies = {
  Strict: {
    config: "SameSite=Strict",
    effect: "完全禁止跨站请求携带Cookie",
    useCase: "高安全要求的操作(如支付、修改密码)"
  },
  Lax: {
    config: "SameSite=Lax", 
    effect: "允许顶级导航的跨站请求携带Cookie",
    useCase: "平衡安全与用户体验的默认选择"
  },
  None: {
    config: "SameSite=None; Secure",
    effect: "允许所有跨站请求携带Cookie",
    useCase: "需要跨站嵌入的第三方服务"
  }
};

2.2 Authorization Token机制: JWT的三重结构

JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。由于这些信息是经过数字签名的,因此可以被验证和信任。

javascript 复制代码
// 一个完整的JWT示例(已解码)
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'

// 对应三个部分:
// 1. Header:    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
// 2. Payload:   eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
// 3. Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header(头部) - 包含令牌类型和签名算法:

json 复制代码
{
  "alg": "HS256",  // 签名算法:HMAC SHA256
  "typ": "JWT"     // 令牌类型
}

2. Payload(载荷) - 包含声明(用户数据和其他元数据):

json 复制代码
{
  "sub": "1234567890",      // 主题(用户ID)
  "name": "John Doe",       // 自定义声明
  "iat": 1516239022,        // 签发时间
  "exp": 1516242622,        // 过期时间
  "role": "admin"           // 用户角色
}

3. Signature(签名) - 用于验证消息在传递过程中没有被篡改:

javascript 复制代码
// 签名生成公式
HMACSHA256(
  base64UrlEncode(header) + "." + 
  base64UrlEncode(payload),
  secret
)

2.3 签名机制:JWT防篡改的核心

JWT最巧妙的设计在于签名机制。服务端用密钥对头部和载荷签名,任何对JWT的修改都会导致签名验证失败。

HMAC vs RSA 算法选择:

  • HMAC(对称加密):签发和验证使用同一个密钥,性能好,适合单体应用
  • RSA(非对称加密):私钥签发,公钥验证,更适合分布式环境
javascript 复制代码
// HMAC签名示例
const crypto = require('crypto');

function signJWT(payload, secret) {
  const header = { alg: 'HS256', typ: 'JWT' };
  const encodedHeader = base64UrlEncode(JSON.stringify(header));
  const encodedPayload = base64UrlEncode(JSON.stringify(payload));
  
  const signature = crypto
    .createHmac('sha256', secret)
    .update(`${encodedHeader}.${encodedPayload}`)
    .digest('base64url');
    
  return `${encodedHeader}.${encodedPayload}.${signature}`;
}

面试重点:base64编码不等于加密!JWT的头部和载荷只是base64编码,任何人都可以解码查看内容。安全性完全依赖于签名机制。

2.4 JWT声明(Claims)的标准与自定义

JWT的Payload包含各种声明(Claims),分为三种类型:

注册声明(标准声明):

json 复制代码
{
  "iss": "auth-service",     // Issuer - 签发者
  "sub": "user123",          // Subject - 主题(用户ID)
  "aud": "web-app",          // Audience - 接收方
  "exp": 1719849622,         // Expiration Time - 过期时间
  "nbf": 1719846022,         // Not Before - 生效时间
  "iat": 1719846022,         // Issued At - 签发时间
  "jti": "a1b2c3d4e5"        // JWT ID - 唯一标识
}

公共声明:可以定义自定义声明,但应避免与标准声明冲突

私有声明:在通信双方之间共享的自定义声明

三、SSO单点登录:Cookie与Token的架构对决

3.1 两种SSO流程对比

Cookie-based SSO传统方案

sequenceDiagram participant U as 用户浏览器 participant A as 应用A(app-a.com) participant SSO as 认证中心(sso.com) participant B as 应用B(app-b.com) U->>A: 访问应用A A->>U: 检查本地Cookie未登录 U->>SSO: 重定向到SSO(携带return_url) SSO->>U: 检查SSO域Cookie已有全局会话 SSO->>U: 生成授权码,重定向回A U->>A: 携带授权码回调 A->>SSO: 后端用授权码换Session票 SSO->>A: 返回用户信息 A->>U: 设置应用A本地Cookie U->>B: 访问应用B B->>U: 检查本地Cookie未登录 U->>SSO: 重定向到SSO(自动带SSO Cookie) SSO->>U: 直接生成授权码重定向回B U->>B: 完成B的本地登录

Token-based SSO现代方案

sequenceDiagram participant U as 用户 participant A as 应用A participant SSO as 认证中心 participant B as 应用B U->>A: 访问应用A A->>U: 前端检查无Token U->>SSO: 弹出登录窗口/重定向 SSO->>U: 认证成功返回Access Token U->>A: 前端存储Token,API调用携带 A->>A: 验证Token签名和声明 U->>B: 访问应用B B->>U: 前端检查无Token U->>SSO: 静默iframe认证(prompt=none) SSO->>U: 返回新的Access Token U->>B: 自动完成B的登录

3.2 技术选型决策树

markdown 复制代码
如果你的项目是:
├── 传统企业内部系统 
│   ├── 域名统一 → Cookie-based SSO
│   └── 浏览器环境固定 → Cookie-based SSO
├── 互联网多产品线
│   ├── 多域名跨域 → Token-based SSO
│   ├── 移动端支持 → Token-based SSO
│   └── 前后端分离 → Token-based SSO
├── 渐进式迁移
│   └── 混合方案(Cookie + Token)
└── 高安全要求
    ├── 金融级安全 → Token-based + 硬件绑定
    └── 政府军工 → 多因素认证 + 短期Token

3.3 实战:JWT 实战

1. Token 安全存储策略

javascript 复制代码
class TokenManager {
  constructor() {
    this.accessToken = null;
    this.refreshToken = null;
  }

  // 存储 access token (内存或 sessionStorage)
  setAccessToken(token) {
    this.accessToken = token;
    // 短期存储,关闭浏览器即清除
    sessionStorage.setItem('access_token', token);
  }

  // 存储 refresh token (更安全的方式)
  setRefreshToken(token) {
    this.refreshToken = token;
    // 使用 httpOnly cookie 是更安全的选择
    this.setHttpOnlyCookie('refresh_token', token, 7);
  }

  // 获取 access token
  getAccessToken() {
    return this.accessToken || sessionStorage.getItem('access_token');
  }

  // 设置 HttpOnly Cookie
  setHttpOnlyCookie(name, value, days) {
    const date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;Secure;SameSite=Strict`;
  }
}

2. 自动 Token 刷新机制

javascript 复制代码
class AuthInterceptor {
  constructor() {
    this.isRefreshing = false;
    this.failedQueue = [];
  }

  // 请求拦截器
  async interceptRequest(config) {
    const token = tokenManager.getAccessToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  }

  // 响应拦截器 - 处理 token 过期
  async interceptResponse(error) {
    const originalRequest = error.config;
    
    if (error.response?.status === 401 && !originalRequest._retry) {
      if (this.isRefreshing) {
        // 如果正在刷新,将请求加入队列
        return new Promise((resolve, reject) => {
          this.failedQueue.push({ resolve, reject });
        }).then(token => {
          originalRequest.headers.Authorization = `Bearer ${token}`;
          return axios(originalRequest);
        });
      }

      originalRequest._retry = true;
      this.isRefreshing = true;

      try {
        const newToken = await this.refreshToken();
        this.isRefreshing = false;
        
        // 重试所有队列中的请求
        this.failedQueue.forEach(pending => pending.resolve(newToken));
        this.failedQueue = [];

        // 重试原始请求
        originalRequest.headers.Authorization = `Bearer ${newToken}`;
        return axios(originalRequest);
      } catch (refreshError) {
        this.isRefreshing = false;
        this.failedQueue.forEach(pending => pending.reject(refreshError));
        this.failedQueue = [];
        
        // 刷新失败,跳转到登录页
        this.handleLogout();
        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  }

  // 刷新 token
  async refreshToken() {
    try {
      const response = await axios.post('/api/auth/refresh', {}, {
        withCredentials: true // 自动发送 refresh token cookie
      });
      
      const { accessToken } = response.data;
      tokenManager.setAccessToken(accessToken);
      return accessToken;
    } catch (error) {
      this.handleLogout();
      throw error;
    }
  }
}

3. 敏感操作 Fresh Token 处理

javascript 复制代码
class SecureOperations {
  // 执行敏感操作前验证 fresh token
  async performSensitiveOperation(operationData) {
    try {
      // 直接尝试执行操作
      return await this.executeOperation(operationData);
    } catch (error) {
      if (error.response?.status === 401 && 
          error.response?.data?.code === 'FRESH_TOKEN_REQUIRED') {
        
        // 需要 fresh token,要求用户重新认证
        const freshToken = await this.requireReauthentication();
        if (freshToken) {
          // 使用 fresh token 重试操作
          return await this.retryOperation(operationData, freshToken);
        }
      }
      throw error;
    }
  }

  // 要求用户重新认证
  async requireReauthentication() {
    return new Promise((resolve) => {
      // 显示重新认证模态框
      this.showReauthModal((password) => {
        if (password) {
          this.getFreshToken(password).then(resolve);
        } else {
          resolve(null);
        }
      });
    });
  }

  // 获取 fresh token
  async getFreshToken(password) {
    const response = await axios.post('/api/auth/fresh-token', {
      password: password
    }, {
      withCredentials: true
    });
    
    const { accessToken } = response.data;
    tokenManager.setAccessToken(accessToken);
    return accessToken;
  }

  // 显示重新认证模态框
  showReauthModal(callback) {
    const modal = document.createElement('div');
    modal.innerHTML = `
      <div class="reauth-modal">
        <h3>需要重新认证</h3>
        <p>出于安全考虑,请重新输入密码</p>
        <input type="password" id="reauth-password" placeholder="输入密码">
        <button onclick="submitReauth()">确认</button>
        <button onclick="cancelReauth()">取消</button>
      </div>
    `;
    
    window.submitReauth = () => {
      const password = document.getElementById('reauth-password').value;
      modal.remove();
      callback(password);
    };
    
    window.cancelReauth = () => {
      modal.remove();
      callback(null);
    };
    
    document.body.appendChild(modal);
  }
}

4. 完整的前端安全封装

javascript 复制代码
class SecureAuthClient {
  constructor() {
    this.tokenManager = new TokenManager();
    this.interceptor = new AuthInterceptor();
    this.secureOps = new SecureOperations();
    this.setupInterceptors();
  }

  setupInterceptors() {
    // axios 请求拦截
    axios.interceptors.request.use(
      config => this.interceptor.interceptRequest(config)
    );

    // axios 响应拦截
    axios.interceptors.response.use(
      response => response,
      error => this.interceptor.interceptResponse(error)
    );

    // 页面可见性变化时检查 token
    document.addEventListener('visibilitychange', () => {
      if (!document.hidden) {
        this.checkTokenValidity();
      }
    });
  }

  // 登录处理
  async login(credentials) {
    const response = await axios.post('/api/auth/login', credentials);
    const { accessToken, refreshToken } = response.data;
    
    this.tokenManager.setAccessToken(accessToken);
    this.tokenManager.setRefreshToken(refreshToken);
    
    return response.data;
  }

  // 登出处理
  async logout() {
    try {
      await axios.post('/api/auth/logout', {}, { 
        withCredentials: true 
      });
    } catch (error) {
      console.error('Logout error:', error);
    } finally {
      this.clearTokens();
      window.location.href = '/login';
    }
  }

  // 清除 tokens
  clearTokens() {
    sessionStorage.removeItem('access_token');
    this.tokenManager.accessToken = null;
    // 清除 refresh token cookie
    document.cookie = 'refresh_token=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/';
  }

  // 定期检查 token 有效性
  async checkTokenValidity() {
    const token = this.tokenManager.getAccessToken();
    if (token) {
      try {
        // 简单验证 token 是否即将过期
        const payload = JSON.parse(atob(token.split('.')[1]));
        const expiresIn = payload.exp * 1000 - Date.now();
        
        if (expiresIn < 5 * 60 * 1000) { // 5分钟内过期
          await this.interceptor.refreshToken();
        }
      } catch (error) {
        console.warn('Token validity check failed:', error);
      }
    }
  }

  // 安全 API 调用封装
  async secureApiCall(url, data, options = {}) {
    const config = {
      url,
      data,
      ...options,
      headers: {
        'X-Requested-With': 'XMLHttpRequest',
        ...options.headers
      }
    };

    if (options.requireFresh) {
      return this.secureOps.performSensitiveOperation({ config });
    }

    return axios(config);
  }
}

// 使用示例
const authClient = new SecureAuthClient();

// 常规 API 调用
authClient.secureApiCall('/api/user/profile')
  .then(response => console.log(response.data));

// 敏感操作(需要 fresh token)
authClient.secureApiCall('/api/user/change-password', {
  newPassword: 'newPassword123'
}, { requireFresh: true })
  .then(response => console.log('Password changed successfully'));

四、安全防护

常见攻击与防护

XSS防护策略:

javascript 复制代码
// 1. 输入验证和输出编码
function sanitizeInput(input) {
  const div = document.createElement('div');
  div.textContent = input;
  return div.innerHTML;
}

// 2. Content Security Policy
// HTTP Header: Content-Security-Policy: default-src 'self'

// 3. 使用httpOnly Cookie存储敏感信息,禁止domain.cookies读取

CSRF防护策略:

javascript 复制代码
// 1. CSRF Token验证
class CSRFTokenService {
  static getToken() {
    let token = document.querySelector('meta[name="csrf-token"]');
    if (!token) {
      token = document.createElement('meta');
      token.name = 'csrf-token';
      token.content = this.generateToken();
      document.head.appendChild(token);
    }
    return token.content;
  }
  
  static generateToken() {
    return crypto.randomUUID();
  }
}

// 2. SameSite Cookie属性
// Set-Cookie: sessionId=abc123; SameSite=Strict

// 3. 验证Referer Header
function verifyReferer(request) {
  const referer = request.headers.referer;
  const allowedOrigins = ['https://yourdomain.com'];
  return allowedOrigins.some(origin => referer.startsWith(origin));
}

五、面试专题:如何展现你的认证架构能力

必知必会的面试题

1. "JWT如何防止篡改?"

加分回答:"JWT通过签名机制防止篡改。服务端使用密钥对Header和Payload进行签名生成Signature,任何对前两部分的修改都会导致签名验证失败。但要注意JWT只是编码而非加密,敏感信息不应放在Payload中。"

2. "前端如何安全存储Token?"

加分回答:"我采用分层策略:开发环境用localStorage方便调试,生产环境用httpOnly Cookie防XSS。同时实现双Token机制,短期Access Token配合长期Refresh Token,并在Token过期前自动刷新,提升用户体验。"

3. "SSO的实现原理?"

加分回答:"SSO的核心是信任转移。Cookie-based SSO依赖同域或父子域Cookie,通过认证中心统一管理会话。Token-based SSO使用JWT标准,适合跨域和微服务场景。选择时要考虑安全要求、技术栈和用户体验的平衡。"

六、总结:认证设计的核心原则

6.1 四大核心原则

  1. 安全第一:默认不信任,验证一切

    • 深度防御,多层防护
    • 最小权限原则
    • 定期安全审计
  2. 用户体验:无感知刷新,平滑降级

    • 自动Token刷新
    • 友好的错误提示
    • 会话恢复机制
  3. 可扩展性:支持业务增长,技术演进

    • 微服务友好设计
    • 水平扩展能力
    • 协议兼容性
  4. 可观测性:全面监控,快速定位

    • 完整的审计日志
    • 实时监控告警
    • 性能指标追踪

6.2 技术选型

需求分析 技术选型 实施计划
用户规模预估 Cookie-based vs Token-based 渐进式迁移策略
安全等级要求 对称加密 vs 非对称加密 回滚方案准备
多端支持需求 集中式 vs 分布式会话 性能压测计划
合规性要求 开源方案 vs 自研 安全渗透测试

思考题

在你的项目中,是如何处理多标签页同时认证的问题?Token在多个标签页间如何同步?

参考方案:

javascript 复制代码
class MultiTabAuthService extends AuthService {
  constructor() {
    super();
    this.setupTabSync();
  }
  
  setupTabSync() {
    // 使用StorageEvent监听其他标签页的存储变化
    window.addEventListener('storage', (event) => {
      if (event.key === 'access_token' && event.newValue) {
        // 其他标签页更新了Token
        this.handleTokenUpdate(event.newValue);
      }
      
      if (event.key === 'auth_logout' && event.newValue) {
        // 其他标签页触发退出
        this.handleGlobalLogout();
      }
    });
    
    // 使用BroadcastChannel进行更及时的通信
    this.broadcastChannel = new BroadcastChannel('auth_sync');
    this.broadcastChannel.onmessage = (event) => {
      if (event.data.type === 'token_update') {
        this.handleTokenUpdate(event.data.token);
      }
    };
  }
  
  setAuthToken(token) {
    super.setAuthToken(token);
    
    // 通知其他标签页
    this.broadcastChannel.postMessage({
      type: 'token_update',
      token: token
    });
  }
  
  logout() {
    // 设置退出标志,通知其他标签页
    localStorage.setItem('auth_logout', Date.now().toString());
    this.broadcastChannel.postMessage({
      type: 'global_logout'
    });
    
    super.logout();
  }
}

在评论区分享你的方案,我们一起探讨最佳实践!

通过本指南,你应该已经建立了从JWT原理到SSO架构的完整知识体系。记住,优秀的认证系统不仅是技术的堆砌,更是安全、体验和可维护性的完美平衡。

相关推荐
code_Bo3 小时前
基于vxe-table进行二次封装
前端·javascript·vue.js
前端一课4 小时前
公开分享一个AI番茄短故事模块技术方案(含代码)
前端
晴殇i4 小时前
为什么现代 JavaScript 代码规范开始建议禁止使用 else ?
前端·javascript·前端框架
源力祁老师4 小时前
OWL与VUE3 的高级组件通信全解析
前端·javascript·vue.js
花开月正圆4 小时前
遇见docker-compose
前端
护国神蛙4 小时前
自动翻译插件中的智能字符串切割方案
前端·javascript·babel
TechFrank4 小时前
浏览器云端写代码,远程开发 Next.js 应用的简易教程
前端
PaytonD4 小时前
LoopBack 2 如何设置静态资源缓存时间
前端·javascript·node.js
snow@li4 小时前
d3.js:学习积累
开发语言·前端·javascript