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
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>
"#;
[package]
name = "example-cors"
version.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true
[dependencies]
salvo = { workspace = true, features = ["cors"] }
tokio = { workspace = true, features = ["macros"] }
tracing.workspace = true
tracing-subscriber.workspace = true
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.