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

Actix-web请求认证正确实现及FromRequest编译问题

Actix-web 实现 FromRequest 获取 CustomClaim 的编译错误解决

问题根源

你对 FromRequest 中 extract 和 from_request 的职责理解有误:

  • extract 是预提取可复用数据的钩子,并非用来存储异步任务供 from_request 调用;
  • 试图将 Pin<Box<dyn Future>> 存入请求扩展并返回引用,但 from_request 要求返回 Self::Future 的所有权,而这类动态异步任务无法实现 Clone,也不能直接返回引用;
  • 代码中滥用 unwrap(),实际场景中会导致 header 不存在/格式错误时直接 panic,不符合健壮性要求。

正确实现方式

方案1:直接在 from_request 中处理异步认证

无需借助 extract 和请求扩展,直接在 from_request 中封装异步认证逻辑即可:

use actix_web::{dev::Payload, Error, FromRequest, HttpRequest};
use std::pin::Pin;
use std::future::Future;

// 示例 CustomClaim 结构体
#[derive(Debug)]
struct CustomClaim;

async fn get_claim(auth_header: &str) -> Result<CustomClaim, Error> {
    // 替换为你的实际认证逻辑
    Ok(CustomClaim)
}

impl FromRequest for CustomClaim {
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;

    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
        // 安全处理 Authorization header
        let auth_header = match req.headers().get("Authorization") {
            Some(header) => match header.to_str() {
                Ok(s) => s.to_owned(),
                Err(e) => return Box::pin(async { Err(Error::from(e)) }),
            },
            None => return Box::pin(async { Err(Error::from("Authorization header missing")) }),
        };

        // 封装异步认证逻辑并返回
        Box::pin(async move {
            get_claim(&auth_header).await
        })
    }
}

方案2:中间件预认证 + 扩展存储结果

如果需要多个提取器共享同一认证结果,可以用中间件提前完成认证,将 CustomClaim 存入请求扩展,再通过 FromRequest 取出:

use actix_web::{dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, FromRequest, HttpRequest, middleware};
use std::pin::Pin;
use std::future::{ready, Future, Ready};

#[derive(Debug, Clone)]
struct CustomClaim;

async fn get_claim(auth_header: &str) -> Result<CustomClaim, Error> {
    // 替换为你的实际认证逻辑
    Ok(CustomClaim)
}

// 认证中间件
#[derive(Clone)]
struct AuthMiddleware;

impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AuthMiddlewareService<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthMiddlewareService { service }))
    }
}

struct AuthMiddlewareService<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let auth_header = match req.headers().get("Authorization") {
            Some(h) => match h.to_str() {
                Ok(s) => s.to_owned(),
                Err(e) => return Box::pin(async { Err(Error::from(e)) }),
            },
            None => return Box::pin(async { Err(Error::from("Authorization header missing")) }),
        };

        let service = self.service.clone();
        Box::pin(async move {
            let claim = get_claim(&auth_header).await?;
            req.extensions_mut().insert(claim);
            service.call(req).await
        })
    }
}

// 实现 FromRequest 从扩展中取出 CustomClaim
impl FromRequest for CustomClaim {
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;

    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
        let claim = req.extensions().get::<CustomClaim>().cloned();
        Box::pin(async move {
            claim.ok_or_else(|| Error::from("CustomClaim not found in request extensions"))
        })
    }
}

// 在 App 中注册中间件
// App::new().wrap(AuthMiddleware)

总结

你不需要将异步任务存入请求扩展,直接在 from_request 中处理认证逻辑是最简洁的方案;如果需要复用认证结果,优先用中间件预认证并存储最终的 CustomClaim(需实现 Clone),而非存储异步任务本身。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 01:10:28