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

新手求助:如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:05:41