You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SvelteKit中WebSockets安全认证方案咨询:AccessToken传递与刷新

解决方案:SvelteKit中WebSocket安全传递AccessToken的实现

浏览器在建立wss://(加密WebSocket)连接时,会自动发送同域的HttpOnly Cookie,前提是Cookie属性配置满足要求:

  • Secure: true:必须在HTTPS环境下生效
  • SameSite: Lax(同域场景)或None(跨域场景需配合Secure)
  • Domain和Path与WebSocket服务地址匹配

后端只需在WebSocket握手的HTTP请求中读取Cookie内的AccessToken,完成身份验证后再建立连接,无需前端手动在消息中传递Token。这种方式完全规避前端直接处理AccessToken的风险,HttpOnly Cookie天然具备防XSS攻击的能力。

二、实现静默Token刷新,避免全页面刷新

基于OIDC协议的静默刷新机制,可在AccessToken过期前自动获取新Token,无需用户交互或全页面刷新:

  1. 集成oidc-client-ts等OIDC客户端库到SvelteKit项目,在根+layout.ts的load函数中初始化客户端
  2. 监听客户端的tokenExpiring事件,触发静默刷新请求(通过iframe向OIDC提供商的授权端点发起请求)
  3. 后端接收新Token后更新HttpOnly Cookie,OIDC客户端会自动将新Token同步到SvelteKit的内存Store中
  4. 当后端检测到Token过期主动断开WebSocket连接时,前端使用更新后的Store值重新发起连接

三、若必须在WebSocket消息中传递Token的安全实践

如果握手携带Cookie不可行(如跨域场景限制),需在消息中传递Token时遵循以下规则:

  • 强制使用wss://协议,确保传输全程加密
  • 仅在连接建立后的第一条消息中传递Token,后续消息无需重复发送
  • 后端严格验证Token的签名、过期时间、受众(aud)等字段,确保有效性
  • 前端将AccessToken存储在内存级Svelte Store中(而非localStorage/sessionStorage),避免XSS攻击导致Token泄露
  • Token过期后,前端先完成静默刷新更新Store,再重新建立WebSocket连接

四、SvelteKit+WebSocket的具体代码示例

1. 内存Store管理AccessToken

// src/stores/auth.ts
import { writable } from 'svelte/store';

export const accessToken = writable<string | null>(null);

2. 静默刷新与WebSocket连接逻辑

<!-- src/routes/+page.svelte -->
<script lang="ts">
  import { accessToken } from '$stores/auth';
  import { oidcClient } from '$lib/oidc';
  import { onMount, onDestroy } from 'svelte';

  let socket: WebSocket | null = null;

  // 初始化WebSocket连接
  const initWebSocket = async () => {
    if (socket) socket.close();
    const currentToken = await accessToken;
    socket = new WebSocket('wss://your-server-domain/ws');
    
    // 仅当握手无法携带Cookie时,在连接成功后发送Token
    socket.onopen = () => {
      currentToken && socket?.send(JSON.stringify({ type: 'auth', token: currentToken }));
    };
    
    // 处理连接断开,尝试刷新Token后重连
    socket.onclose = async () => {
      try {
        // 静默刷新Token
        await oidcClient.signinSilent();
        const newToken = oidcClient.tokenStore.getAccessToken();
        accessToken.set(newToken);
        setTimeout(initWebSocket, 1000);
      } catch {
        // 刷新失败,引导用户重新登录
        window.location.href = '/login';
      }
    };
  };

  // 监听Token更新,自动重连WebSocket
  accessToken.subscribe(() => {
    if (socket?.readyState === WebSocket.OPEN) {
      socket.close();
      initWebSocket();
    }
  });

  onMount(() => {
    initWebSocket();
    // 监听Token即将过期事件,提前触发刷新
    oidcClient.events.addTokenExpiring(() => {
      oidcClient.signinSilent().catch(() => {});
    });
  });

  onDestroy(() => {
    socket?.close();
  });
</script>

3. 后端WebSocket握手验证示例(Node.js)

// 后端WebSocket服务器代码
import { WebSocketServer } from 'ws';
import jwt from 'jsonwebtoken';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws, req) => {
  // 从握手请求的Cookie中读取AccessToken
  const cookieHeader = req.headers.cookie;
  const token = cookieHeader?.split('access_token=')[1]?.split(';')[0];
  
  if (!token) {
    ws.close(401, 'Unauthorized');
    return;
  }

  try {
    // 验证Token有效性
    jwt.verify(token, process.env.JWT_SECRET);
    // 验证通过,处理后续消息
    ws.on('message', (data) => {
      // 业务逻辑处理
    });
  } catch {
    ws.close(401, 'Invalid Token');
  }
});

内容的提问来源于stack exchange,提问作者Trilleo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:04:59