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
相关产品推荐
相关产品推荐

