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

如何在Actix Web中创建可读取Cookie的授权中间件

在Actix-web中用全局中间件自动处理HttpOnly Cookie授权

直接上解决方案:自定义Actix-web中间件,在请求到达处理器前自动完成Cookie提取、授权验证,未授权请求直接拦截返回错误,授权通过则放行,处理器完全不用关心授权逻辑。

实现步骤

1. 定义授权相关结构体

先定义存储用户信息的结构体(用于验证后传递给处理器,不需要的话可以省略),以及中间件需要的类型:

use actix_web::{
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
    Error, HttpResponse, web,
};
use futures_util::future::{FutureExt, LocalBoxFuture};
use std::task::{Context, Poll};

// 示例:验证通过后的用户信息,可根据实际需求定义
#[derive(Debug, Clone)]
struct AuthenticatedUser {
    user_id: u64,
    username: String,
}

// 中间件结构体,无状态的话可以是空结构体
struct AuthMiddleware;

2. 实现中间件的Transform和Service逻辑

Transform用于包装现有服务,Service则处理具体的请求拦截和授权逻辑:

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 = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        async move { Ok(AuthMiddlewareService { service }) }.boxed_local()
    }
}

// 包装后的服务结构体
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 = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

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

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // 1. 从请求中提取HttpOnly Cookie
        let auth_cookie = req.cookie("auth_session"); // 替换成你的Cookie名称

        // 2. 执行授权验证逻辑
        let auth_result = match auth_cookie {
            Some(cookie) => {
                // 示例:验证Cookie值,比如解析JWT、查询Redis/DB验证会话有效性
                // 这里替换成你的实际授权逻辑
                if cookie.value() == "valid_session_token" {
                    Ok(AuthenticatedUser {
                        user_id: 123,
                        username: "finlay_weber".to_string(),
                    })
                } else {
                    Err("无效的会话令牌")
                }
            }
            None => Err("未找到授权Cookie"),
        };

        // 3. 根据验证结果处理请求
        match auth_result {
            Ok(user) => {
                // 授权通过:将用户信息存入请求扩展,处理器可按需获取(不需要的话可省略此步)
                req.extensions_mut().insert(user);
                // 继续将请求传递给后续服务/处理器
                let fut = self.service.call(req);
                async move { fut.await }.boxed_local()
            }
            Err(msg) => {
                // 授权失败:直接返回401响应,不进入处理器
                async move {
                    Ok(req.into_response(
                        HttpResponse::Unauthorized()
                            .body(format!("授权失败:{}", msg))
                            .into_body(),
                    ))
                }.boxed_local()
            }
        }
    }
}

3. 注册中间件到Actix-web应用

将中间件全局注册到App,或者针对特定路由组注册,这样所有匹配的请求都会先经过授权验证:

use actix_web::{App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            // 注册全局授权中间件,所有请求都会经过验证
            .wrap(AuthMiddleware)
            // 定义你的路由,处理器无需包含授权逻辑
            .route("/protected", web::get().to(protected_handler))
            .route("/public", web::get().to(public_handler))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

// 受保护的处理器:无需写授权逻辑,中间件已拦截未授权请求
async fn protected_handler() -> HttpResponse {
    HttpResponse::Ok().body("已授权访问受保护资源")
}

// 公开处理器:如果需要排除某些路由,可以将中间件注册到路由组而非全局
async fn public_handler() -> HttpResponse {
    HttpResponse::Ok().body("公开资源,无需授权")
}

关键说明

  • 中间件会在请求到达处理器之前完成所有授权操作,未授权请求直接返回错误,不会进入处理器逻辑。
  • 如果处理器需要使用用户信息,可以从请求的Extensions中提取,比如:
    async fn protected_handler(web::ReqData(user): web::ReqData<AuthenticatedUser>) -> HttpResponse {
        HttpResponse::Ok().body(format!("欢迎,{}", user.username))
    }
    
  • 若只需拦截未授权请求,不需要传递用户信息,可省略extensions_mut().insert(user)的步骤。
  • 针对无需授权的路由(如登录、公开接口),可以将中间件注册到特定路由组,而非全局,比如:
    App::new()
        .service(web::scope("/api/protected")
            .wrap(AuthMiddleware)
            .route("/data", web::get().to(protected_handler)))
        .service(web::scope("/api/public")
            .route("/info", web::get().to(public_handler)))
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:35:07