新手求助:如何在actix-web中实现基础HTTP认证
Actix-Web 基础HTTP认证实现方案(适配环境变量+会话存储)
第一步:添加依赖
在Cargo.toml中引入所需依赖:
[dependencies] actix-web = "4" actix-session = "0.7" actix-web-httpauth = "0.8" dotenv = "0.15" serde = { version = "1.0", features = ["derive"] }
第二步:读取Docker环境变量
通过dotenv加载环境变量(本地开发也能兼容,Docker运行时自动读取容器环境变量),提取预设的用户名和密码:
use dotenv::dotenv; use std::env; fn get_credentials() -> (String, String) { dotenv().ok(); // 本地开发加载.env文件,Docker环境下无影响 let username = env::var("AUTH_USERNAME").expect("AUTH_USERNAME环境变量未设置"); let password = env::var("AUTH_PASSWORD").expect("AUTH_PASSWORD环境变量未设置"); (username, password) }
第三步:实现认证中间件
自定义中间件优先检查会话中的认证状态,未认证则校验Basic Auth凭证,通过后将状态写入会话:
use actix_web::{ dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, HttpResponse, }; use actix_web::http::header::{WWW_AUTHENTICATE, HeaderValue}; use actix_web_httpauth::extractors::basic::BasicAuth; use actix_session::Session; use futures_util::future::LocalBoxFuture; use std::future::{ready, Ready}; // 中间件结构体 pub struct AuthMiddleware; // 实现Transform trait创建中间件 impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + '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 })) } } // 中间件服务结构体 pub struct AuthMiddlewareService<S> { service: S, } impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<S> where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + '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 std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> { self.service.poll_ready(cx) } fn call(&self, req: ServiceRequest) -> Self::Future { let (username, password) = get_credentials(); let session = req.get_session(); // 检查会话是否已认证 if session.get::<bool>("authenticated").unwrap_or(false) { let fut = self.service.call(req); return Box::pin(async move { fut.await }); } // 提取Basic Auth凭证并校验 match BasicAuth::extract(&req) { Ok(auth) => { if auth.username() == username && auth.password() == Some(&password) { // 认证成功,写入会话 let mut session = req.get_session(); let _ = session.insert("authenticated", true); let fut = self.service.call(req); Box::pin(async move { fut.await }) } else { Box::pin(async { let res = HttpResponse::Unauthorized() .append_header((WWW_AUTHENTICATE, HeaderValue::from_static("Basic realm=\"Restricted Area\""))) .finish(); Ok(req.into_response(res)) }) } } Err(_) => { Box::pin(async { let res = HttpResponse::Unauthorized() .append_header((WWW_AUTHENTICATE, HeaderValue::from_static("Basic realm=\"Restricted Area\""))) .finish(); Ok(req.into_response(res)) }) } } } }
第四步:配置会话与路由
在主函数中配置Cookie会话存储,划分公开路由和需认证的/api路由组:
use actix_web::{App, HttpServer}; use actix_session::storage::CookieSessionStore; use actix_session::SessionMiddleware; use actix_web::cookie::Key; #[actix_web::main] async fn main() -> std::io::Result<()> { // 生成会话加密密钥(生产环境建议从环境变量读取固定密钥) let secret_key = Key::generate(); HttpServer::new(move || { App::new() // 配置会话中间件,存储认证状态 .wrap(SessionMiddleware::new( CookieSessionStore::default(), secret_key.clone() )) // 公开路由:无需认证 .route("/", web::get().to(|| async { "公开页面,无需认证" })) .route("/about", web::get().to(|| async { "关于页面,无需认证" })) // 需要认证的/api路由组 .service( web::scope("/api") .wrap(AuthMiddleware) .route("/user", web::get().to(|| async { "API用户数据,已认证" })) .route("/settings", web::get().to(|| async { "API设置页面,已认证" })) ) }) .bind(("0.0.0.0", 8080))? .run() .await }
第五步:Docker运行配置
可以在Dockerfile中预设环境变量,或运行容器时动态传递:
Dockerfile示例片段
FROM rust:1.70 as builder # 构建项目逻辑... FROM debian:bookworm-slim # 复制二进制文件... ENV AUTH_USERNAME=admin ENV AUTH_PASSWORD=your_secure_password CMD ["./your_app_binary"]
动态传递环境变量运行容器
docker run -p 8080:8080 -e AUTH_USERNAME=admin -e AUTH_PASSWORD=123456 your_image_name
注意事项
- 生产环境不要用生成的临时密钥,建议从环境变量读取固定密钥,用
Key::from(&[])加载 - Basic Auth凭证是Base64编码的明文,生产环境必须搭配HTTPS使用
- 如需注销功能,只需清除会话中的
authenticated字段即可
内容的提问来源于stack exchange,提问作者Sntn
相关产品推荐
相关产品推荐

