基本權限驗證

提供對 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
basic-auth/src/main.rs
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 5800 and start serving
    let acceptor = TcpListener::new("0.0.0.0:5800").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:5800/")
            .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:5800/")
            .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"));
    }
}