基本权限验证

提供对 Basic Auth 的支持的中间件.

Basic Auth 简介

Basic Auth 是一种简单的 HTTP 认证机制,通过在 HTTP 请求头中包含 Authorization 字段来传递用户名和密码。格式为 Authorization: Basic <base64 username:password>。虽然简单,但由于凭证仅进行 Base64 编码而非加密,通常需要与 HTTPS 一起使用以确保安全。

常见框架中的 Basic Auth 实现对比

框架语言Basic Auth 实现
SalvoRust通过 BasicAuth 中间件和自定义 BasicAuthValidator 实现
ExpressJavaScript使用 express-basic-auth 中间件包
Spring SecurityJava通过 httpBasic() 配置和 UserDetailsService 实现
ASP.NET CoreC#使用 .UseAuthentication()AddAuthentication(AuthenticationSchemes.Basic)
GinGo通过自定义中间件或使用 gin-contrib/auth

使用场景

Basic Auth 适用于以下场景:

  1. 内部 API 和工具:公司内部使用的管理工具和 API
  2. 开发和测试环境:快速实现认证而不需要复杂的登录系统
  3. 简单的 API 保护:当不需要复杂的用户管理系统时
  4. 与其他安全措施配合:作为多层安全架构的一部分

在 Salvo 中,Basic Auth 中间件可以轻松集成到路由中,通过实现 BasicAuthValidator trait 来自定义验证逻辑,非常灵活。

注意事项

  • 始终与 HTTPS 一起使用以保护凭证传输
  • 不适合存储敏感信息的生产环境
  • 考虑使用更安全的认证方式如 JWT、OAuth 等用于生产环境
main.rs
Cargo.toml
use salvo::basic_auth::{BasicAuth, BasicAuthValidator};
use salvo::prelude::*;

// Custom validator implementing BasicAuthValidator trait
struct Validator;
impl BasicAuthValidator for Validator {
    // Validate username and password combination
    async fn validate(&self, username: &str, password: &str, _depot: &mut Depot) -> bool {
        username == "root" && password == "pwd"
    }
}

// Simple handler that returns "Hello" for authenticated requests
#[handler]
async fn hello() -> &'static str {
    "Hello"
}

// Create router with basic authentication middleware
fn route() -> Router {
    // Initialize basic authentication handler with our validator
    let auth_handler = BasicAuth::new(Validator);
    // Apply authentication middleware to the router
    Router::with_hoop(auth_handler).goal(hello)
}

#[tokio::main]
async fn main() {
    // Initialize logging
    tracing_subscriber::fmt().init();

    // Bind server to port 8698 and start serving
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(route()).await;
}

#[cfg(test)]
mod tests {
    use salvo::prelude::*;
    use salvo::test::{ResponseExt, TestClient};

    #[tokio::test]
    async fn test_basic_auth() {
        // Create a service instance from our router for testing purposes
        let service = Service::new(super::route());

        // Test case 1: Verify successful authentication with valid credentials
        let content = TestClient::get("http://0.0.0.0:8698/")
            .basic_auth("root", Some("pwd")) // Use correct username/password
            .send(&service) // Send the request to the service
            .await
            .take_string() // Extract response body as string
            .await
            .unwrap();
        // Verify response contains expected "Hello" message
        assert!(content.contains("Hello"));

        // Test case 2: Verify authentication failure with invalid password
        let content = TestClient::get("http://0.0.0.0:8698/")
            .basic_auth("root", Some("pwd2")) // Use incorrect password
            .send(&service) // Send the request to the service
            .await
            .take_string() // Extract response body as string
            .await
            .unwrap();
        // Verify response contains "Unauthorized" error
        assert!(content.contains("Unauthorized"));
    }
}