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

Rust跨平台客户端WebSocket连接401未授权问题求助

问题分析

核心矛盾是Wasm与Native客户端的Cookie传递机制差异:

  • Wasm端运行在浏览器环境,浏览器会自动在WebSocket握手的HTTP请求中附带登录后生成的Session Cookie,因此能通过服务端的RequireAuthorizationLayer认证
  • Native端的ewebsock基于tokio-tungstenite,不会自动读取reqwest的CookieStore,导致WebSocket握手请求不带认证Cookie,触发服务端401 Unauthorized
解决方案

方案1:手动传递Session Cookie到WebSocket连接

直接从reqwest的Cookie存储中提取Session Cookie,在建立WebSocket连接时手动添加到请求头:

// 初始化带Cookie存储的reqwest客户端
let client = reqwest::Client::builder()
    .cookie_store(true)
    .build()?;

// 登录成功后,提取Session Cookie(axum_sessions默认Cookie名为"session")
let session_cookie = client.cookie_store()
    .and_then(|store| store.get("localhost", "/", "session"))
    .ok_or_else(|| anyhow::anyhow!("未找到Session Cookie"))?;

// 构建WebSocket请求头
let mut ws_headers = ewebsock::Headers::new();
ws_headers.insert(
    "Cookie",
    format!("{}={}", session_cookie.name(), session_cookie.value())
);

// 带自定义头建立WebSocket连接
let (sender, receiver) = ewebsock::connect_with_headers(
    "ws://localhost:8080/websocket",
    ws_headers,
).await?;

方案2:统一跨平台Cookie管理

针对Wasm/Native双目标实现Cookie共享逻辑:

  • Wasm端:依赖浏览器自动处理Cookie传递,直接调用ewebsock::connect即可
  • Native端:维护全局共享的Cookie存储,让reqwest和ewebsock共用同一套Cookie数据
#[cfg(not(target_arch = "wasm32"))]
use reqwest_cookie_store::CookieStore;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::{Arc, Mutex};

// 跨平台共享Cookie存储
#[derive(Clone)]
struct SharedCookieStore {
    #[cfg(not(target_arch = "wasm32"))]
    inner: Arc<Mutex<CookieStore>>,
}

#[cfg(not(target_arch = "wasm32"))]
impl SharedCookieStore {
    // 提取Cookie并生成WebSocket请求头
    pub fn get_ws_headers(&self) -> ewebsock::Headers {
        let store = self.inner.lock().unwrap();
        let mut headers = ewebsock::Headers::new();
        if let Some(cookie) = store.get("localhost", "/", "session") {
            headers.insert("Cookie", format!("{}={}", cookie.name(), cookie.value()));
        }
        headers
    }
}

// Native端使用示例
let cookie_store = SharedCookieStore {
    inner: Arc::new(Mutex::new(CookieStore::default())),
};
// 初始化reqwest客户端时绑定该存储
let client = reqwest::Client::builder()
    .cookie_provider(cookie_store.inner.clone())
    .build()?;
// 建立WebSocket时使用存储生成的头
let headers = cookie_store.get_ws_headers();
let (sender, receiver) = ewebsock::connect_with_headers("ws://localhost:8080/websocket", headers).await?;

方案3:切换为Token认证(替代Cookie)

如果不想处理Cookie跨平台问题,可改用JWT Token认证:

  1. 服务端登录接口返回JWT Token
  2. 客户端在WebSocket握手时添加Authorization: Bearer <token>请求头
  3. 服务端修改认证逻辑,同时支持Cookie和Token验证

服务端认证层修改示例:

async fn authorize_request(req: &Request<()>) -> Result<UserId, AuthError> {
    // 优先尝试Cookie会话认证
    if let Ok(user_id) = axum_login::extract_user_id(req).await {
        return Ok(user_id);
    }
    // 再尝试Token认证
    if let Some(auth_header) = req.headers().get(axum::http::header::AUTHORIZATION) {
        let token = auth_header.to_str()?.strip_prefix("Bearer ")?.to_string();
        let claims = verify_jwt_token(&token)?; // 自定义JWT验证逻辑
        return Ok(claims.user_id);
    }
    Err(AuthError::Unauthorized)
}

// 将自定义认证层应用到WebSocket路由
.route_layer(axum::middleware::from_fn(authorize_request))
验证步骤
  1. 确认Native客户端登录后,Session Cookie已被reqwest的CookieStore正确存储
  2. 用抓包工具(如Wireshark)检查WebSocket握手请求的Cookie头是否存在
  3. 查看服务端日志,确认认证层是否接收到有效认证信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:57:42