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

替换后端(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:36:00