如何在Actix Web中创建可读取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
相关产品推荐
相关产品推荐

