Contrôle d'Accès Inter-Origine

CORS (Cross-Origin Resource Sharing) est un mécanisme permettant aux navigateurs d'effectuer des requêtes vers des serveurs d'origine différente, surmontant ainsi les restrictions imposées par la politique de même origine des navigateurs.

Qu'est-ce que la Politique de Même Origine ?

La politique de même origine est une fonctionnalité de sécurité des navigateurs qui limite la manière dont les documents ou scripts chargés depuis une origine peuvent interagir avec des ressources provenant d'une autre origine. "Même origine" signifie avoir le même protocole, domaine et numéro de port.

Pourquoi CORS est-il Nécessaire ?

La prise en charge de CORS est requise lorsqu'une application frontend doit accéder à des API depuis une origine différente. Par exemple :

  • L'application frontend est déployée sur https://frontend.com
  • Le service API est déployé sur https://api.backend.com

Sans CORS, le navigateur empêchera l'application frontend d'accéder au service API.

Fonctionnement de CORS

CORS implémente le contrôle d'accès inter-origine via une série d'en-têtes HTTP :

  • Requêtes Simples : Envoyées directement, le serveur contrôlant l'autorisation d'accès via les en-têtes de réponse.
  • Requêtes Préliminaires : Le navigateur envoie d'abord une requête OPTIONS pour demander au serveur si l'accès inter-origine est autorisé, et n'envoie la requête réelle qu'après avoir reçu la permission.

Puisque le navigateur envoie des requêtes préliminaires avec Method::OPTIONS, il est nécessaire de traiter ces requêtes. Le middleware CORS doit être ajouté au Service.

Utilisation de CORS dans Salvo

Salvo fournit un middleware CORS intégré qui peut être facilement configuré et utilisé. Voici un exemple de code :

Warning

Remarque : Le middleware .hoop(cors) est appliqué au Service, et non au Router. Cela garantit le traitement automatique des requêtes préliminaires OPTIONS.

let cors = Cors::new()
    .allow_origin(["http://127.0.0.1:8698", "http://localhost:8698"])
    .allow_methods(vec![Method::GET, Method::POST, Method::DELETE])
    .allow_headers("authorization")
    .into_handler();

// Configuration du routeur backend avec protection CORS
let router = Router::with_path("hello").post(hello);
let service = Service::new(router).hoop(cors);

Exemple de Code

main.rs
Cargo.toml
use salvo::cors::Cors;
use salvo::http::Method;
use salvo::prelude::*;

#[tokio::main]
async fn main() {
    // Initialize logging system
    tracing_subscriber::fmt().init();
    // Start both backend and frontend servers concurrently
    tokio::join!(backend_server(), frontend_server());
}

async fn backend_server() {
    // Handler that returns a simple message for CORS demonstration
    #[handler]
    async fn hello() -> &'static str {
        "hello, I am content from remote server."
    }

    // Configure CORS middleware with specific settings:
    // - Allow requests from localhost:8698
    // - Allow specific HTTP methods
    // - Allow authorization header
    let cors = Cors::new()
        .allow_origin(["http://127.0.0.1:8698", "http://localhost:8698"])
        .allow_methods(vec![Method::GET, Method::POST, Method::DELETE])
        .allow_headers("authorization")
        .into_handler();

    // Set up backend router with CORS protection
    let router = Router::with_path("hello").post(hello);
    let service = Service::new(router).hoop(cors);

    // Start backend server on port 5600
    let acceptor = TcpListener::new("0.0.0.0:5600").bind().await;
    Server::new(acceptor).serve(service).await;
}

async fn frontend_server() {
    // Handler that serves the HTML page with CORS test
    #[handler]
    async fn index() -> Text<&'static str> {
        Text::Html(HTML_DATA)
    }

    // Set up frontend router to serve the test page
    let router = Router::new().get(index);
    // Start frontend server on port 8698
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}

// HTML template with JavaScript code to test CORS
// Contains a button that triggers a POST request to the backend server
const HTML_DATA: &str = r#"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Salvo Cors</title>
</head>
<body>
<button id="btn">Load Content</button>
<div id="content"></div>
<script>
document.getElementById("btn").addEventListener("click", function() {
    fetch("http://127.0.0.1:5600/hello", {method: "POST", headers: {authorization: "abcdef"}}).then(function(response) {
        return response.text();
    }).then(function(data) {
        document.getElementById("content").innerHTML = data;
    });
});
</script>
</body>
</html>
"#;

Principales Options de Configuration

Le middleware CORS offre diverses options de configuration :

  • Origines Autorisées : Contrôle quels domaines peuvent accéder aux ressources.
  • Méthodes Autorisées : Spécifie les méthodes HTTP autorisées (GET, POST, etc.).
  • En-têtes Autorisés : Spécifie les en-têtes de requête autorisés.
  • En-têtes Exposés : Spécifie quels en-têtes de réponse peuvent être consultés par le client.
  • Autorisation des Identifiants : Détermine si les requêtes peuvent inclure des identifiants tels que des cookies.
  • Durée de Cache des Requêtes Préliminaires : Durée de mise en cache des résultats des requêtes préliminaires.

En configurant CORS de manière appropriée, vous pouvez garantir à la fois la sécurité et la capacité à répondre aux exigences d'accès inter-origine.