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认证:
- 服务端登录接口返回JWT Token
- 客户端在WebSocket握手时添加
Authorization: Bearer <token>请求头 - 服务端修改认证逻辑,同时支持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))
验证步骤
- 确认Native客户端登录后,Session Cookie已被
reqwest的CookieStore正确存储 - 用抓包工具(如Wireshark)检查WebSocket握手请求的
Cookie头是否存在 - 查看服务端日志,确认认证层是否接收到有效认证信息
内容的提问来源于stack exchange,提问作者Bryan Reilly
相关产品推荐
相关产品推荐

