替换后端(Node.js→Rust)后前端登录状态逻辑异常求助
问题:Node.js(Express)迁移到Rust(Actix Web)后前端路由异常
未授权用户访问/login页面时,被错误重定向到仅授权用户可访问的/页面,前端代码未做任何修改,问题由后端替换操作引发。
前端通过调用登录状态校验API,用setLoggedIn方法根据响应管理登录状态,但异常仍未解决。
原Node.js(Express)登录状态校验实现
// login status export const userLoginStatus = asyncHandler(async (req, res) => { const token = req.cookies.token; if (!token) { // 401 Unauthorized res.status(401).json({ message: "Not authorized, please login!" }); } // verify the token const decoded = jwt.verify(token, process.env.JWT_SECRET); if (decoded) { res.status(200).json(true); } else { res.status(401).json(false); } });
新Rust(Actix Web)相关实现
登录状态接口
pub async fn login_status( req: HttpRequest, ) -> impl Responder { match jwt::verify(&req) { Ok(_claims) => { success_log!("[user_handler] - [login_status] message: Authorized!"); HttpResponse::Ok().json(true) }, Err(_) => { error_log!("[user_handler] - [login_status] Not authorized"); HttpResponse::Unauthorized().json(false) } } }
JWT校验逻辑
pub fn verify <R: RequestHeaders>(req: &R) -> Result<Claims, String> { // Extract a token from request header if let Some(auth_header) = req.get_headers().get("Authorization") { if let Ok(auth_str) = auth_header.to_str() { let parts: Vec<&str> = auth_str.split_whitespace().collect(); if parts.len() == 2 && parts[0] == "Bearer" { let token = parts[1]; // decode uesr info match decode_token(token) { Ok(user_info) => { return Ok(user_info.claims); }, Err(error) => { error_log!("[jwt] - [verify] error = {}", error); return Err(error.to_string()); } } } } } return Err(AUTH_MSG.get("TOKEN_NOT_FOUND_IN_REQUEST_HEADER_MSG").unwrap_or(&"").to_string()); }
JWT工具函数
use jsonwebtoken::{encode, decode, Header, Algorithm, EncodingKey, DecodingKey, Validation, TokenData}; /// JWT Claims /// /// * `id` - ID. /// * `sub` - email /// * `exp` - expiration #[derive(Serialize, Deserialize, Debug)] pub struct Claims { pub id: i32, pub sub: String, pub exp: usize, } pub trait RequestHeaders { fn get_headers(&self) -> &HeaderMap; } impl RequestHeaders for HttpRequest { fn get_headers(&self) -> &HeaderMap { self.headers() } } impl RequestHeaders for ServiceRequest { fn get_headers(&self) -> &HeaderMap { self.headers() } } pub fn create_token(email: &str, id: &i32) -> Result<String, jsonwebtoken::errors::Error> { // token due date 10 days let days = 60 * 60 * 24 * 10; let expiration = SystemTime::now() + Duration::from_secs(days); let claims = Claims { id: id.to_owned(), sub: email.to_owned(), exp: expiration.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as usize, }; encode( &Header::default(), &claims, &EncodingKey::from_secret("secret".as_ref()) ) } pub fn decode_token(token: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> { decode::<Claims>( token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256) ) }
问题根源与修复方案
问题核心
前后端实现的Token获取位置不一致:
- Node.js版本从
Cookie中提取Token(req.cookies.token) - Rust版本仅从
Authorization请求头的Bearer格式中提取Token
前端未修改,仍将Token存在Cookie中,导致Rust后端始终无法找到Token,返回401状态码并响应false。而前端setLoggedIn可能存在逻辑错误——比如忽略状态码,仅根据响应体false错误判定登录状态,触发重定向。
修复方式
方式一:修改Rust后端兼容Cookie获取(推荐)
调整verify函数,优先从Cookie中提取Token,保持和原Node.js逻辑一致:
pub fn verify <R: RequestHeaders>(req: &R) -> Result<Claims, String> { // 先从Cookie中获取Token(兼容原逻辑) if let Some(cookie_header) = req.get_headers().get("Cookie") { if let Ok(cookie_str) = cookie_header.to_str() { for cookie in cookie_str.split(';') { let parts: Vec<&str> = cookie.trim().split('=').collect(); if parts.len() == 2 && parts[0] == "token" { let token = parts[1]; match decode_token(token) { Ok(user_info) => return Ok(user_info.claims), Err(error) => { error_log!("[jwt] - [verify] error = {}", error); return Err(error.to_string()); } } } } } } // 保留原Authorization头逻辑(可选) if let Some(auth_header) = req.get_headers().get("Authorization") { if let Ok(auth_str) = auth_header.to_str() { let parts: Vec<&str> = auth_str.split_whitespace().collect(); if parts.len() == 2 && parts[0] == "Bearer" { let token = parts[1]; match decode_token(token) { Ok(user_info) => return Ok(user_info.claims), Err(error) => { error_log!("[jwt] - [verify] error = {}", error); return Err(error.to_string()); } } } } } return Err(AUTH_MSG.get("TOKEN_NOT_FOUND_IN_REQUEST_HEADER_MSG").unwrap_or(&"").to_string()); }
方式二:检查前端状态判断逻辑
确保前端setLoggedIn函数优先根据响应状态码判断:当后端返回401时,无论响应体是什么,都将登录状态设为false。
内容的提问来源于stack exchange,提问作者OMZ
相关产品推荐
相关产品推荐

