Autenticação Básica

Middleware que fornece suporte a Basic Auth.

Introdução à Autenticação Básica

Basic Auth é um mecanismo simples de autenticação HTTP que transmite nome de usuário e senha através do cabeçalho Authorization nas requisições HTTP. O formato é Authorization: Basic <base64(username:password)>. Embora simples, como as credenciais são apenas codificadas em Base64 e não criptografadas, geralmente é necessário usar com HTTPS para garantir segurança.

Comparação de implementações de Basic Auth em frameworks comuns

FrameworkLinguagemImplementação Basic Auth
SalvoRustAtravés do middleware BasicAuth e implementação personalizada de BasicAuthValidator
ExpressJavaScriptUsando o pacote middleware express-basic-auth
Spring SecurityJavaConfiguração via httpBasic() e implementação de UserDetailsService
ASP.NET CoreC#Usando .UseAuthentication() e AddAuthentication(AuthenticationSchemes.Basic)
GinGoAtravés de middleware personalizado ou usando o pacote gin-contrib/auth

Cenários de uso

Basic Auth é adequado para os seguintes cenários:

  1. APIs e ferramentas internas: Ferramentas administrativas e APIs de uso interno da empresa
  2. Ambientes de desenvolvimento e teste: Implementação rápida de autenticação sem sistemas complexos de login
  3. Proteção simples de APIs: Quando não é necessário um sistema complexo de gerenciamento de usuários
  4. Combinação com outras medidas de segurança: Como parte de uma arquitetura de segurança em camadas

No Salvo, o middleware Basic Auth pode ser facilmente integrado às rotas, com lógica de validação personalizada através da implementação da trait BasicAuthValidator, oferecendo grande flexibilidade.

Considerações importantes

  • Sempre use com HTTPS para proteger a transmissão das credenciais
  • Não é adequado para ambientes de produção com informações sensíveis
  • Considere usar métodos de autenticação mais seguros como JWT, OAuth etc. para ambientes de produção
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"));
    }
}