Contrôle interdomaine (CORS)
Le CORS (Cross-Origin Resource Sharing, partage des ressources entre origines multiples) est un mécanisme qui permet aux navigateurs d'effectuer des requêtes vers des serveurs d'origines différentes, surmontant ainsi les restrictions de 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 un document ou un script chargé depuis une origine peut interagir avec des ressources provenant d'une autre origine. Par "même origine", on entend le même protocole, domaine et numéro de port.
Pourquoi avons-nous besoin du CORS ?
Lorsqu'une application frontale a besoin d'accéder à des API d'origines différentes, le CORS devient nécessaire. Par exemple :
- L'application frontale est déployée sur
https://frontend.com
- Le service API est déployé sur
https://api.backend.com
Sans CORS, le navigateur empêcherait l'application frontale d'accéder au service API.
Fonctionnement du CORS
Le CORS utilise une série d'en-têtes HTTP pour contrôler l'accès interdomaine :
- Requête simple : envoyée directement, le serveur contrôle l'autorisation via les en-têtes de réponse
- Requête préliminaire (preflight) : le navigateur envoie d'abord une requête OPTIONS pour demander l'autorisation, puis envoie la requête réelle après approbation
Comme le navigateur envoie une requête préliminaire Method::OPTIONS
, il faut ajouter un traitement pour ce type de requête en ajoutant le middleware CORS au Service
.
Utilisation du CORS dans Salvo
Salvo fournit un middleware CORS intégré, facile à configurer et utiliser. Voici un exemple de code :
WARNING
Remarque. Le middleware .hoop(cors) s'applique au Service
, le middleware .hoop(cors) s'applique au Service
. Le middleware .hoop(cors) s'applique au Service
, et non au Router
.
Cela permet de gérer automatiquement les requêtes préliminaires OPTIONS.
let cors = Cors::new()
.allow_origin(["http://127.0.0.1:5800", "http://localhost:5800"])
.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
cors/src/main.rs
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:5800
// - Allow specific HTTP methods
// - Allow authorization header
let cors = Cors::new()
.allow_origin(["http://127.0.0.1:5800", "http://localhost:5800"])
.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 5800
let acceptor = TcpListener::new("0.0.0.0:5800").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>
"#;
Options de configuration principales
Le middleware CORS offre plusieurs 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 permises (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 accessibles par le client
- Autorisation des informations d'identification : si les requêtes peuvent inclure des cookies ou autres informations d'authentification
- Durée de cache des requêtes préliminaires : durée de mise en cache des résultats des requêtes préliminaires
Une configuration appropriée du CORS permet d'assurer à la fois la sécurité et les besoins d'accès interdomaine.