Control de Origen Cruzado
CORS (Intercambio de Recursos de Origen Cruzado) es un mecanismo que permite a los navegadores realizar solicitudes a servidores de origen cruzado, superando así las restricciones impuestas por la política de mismo origen del navegador.
¿Qué es la Política de Mismo Origen?
La política de mismo origen es una característica de seguridad de los navegadores que restringe cómo los documentos o scripts cargados desde un origen pueden interactuar con recursos de otro origen. "Mismo origen" se refiere a tener el mismo protocolo, dominio y número de puerto.
¿Por qué se Necesita CORS?
El soporte de CORS es necesario cuando una aplicación frontend necesita acceder a APIs de un origen diferente. Por ejemplo:
- La aplicación frontend está desplegada en
https://frontend.com
- El servicio API está desplegado en
https://api.backend.com
Sin CORS, el navegador impedirá que la aplicación frontend acceda al servicio API.
Cómo Funciona CORS
CORS implementa el control de acceso de origen cruzado a través de una serie de encabezados HTTP:
- Solicitudes Simples: Se envían directamente, con el servidor controlando si se permite el acceso mediante encabezados de respuesta.
- Solicitudes Preflight: El navegador primero envía una solicitud OPTIONS para preguntar al servidor si se permite el acceso de origen cruzado, y solo envía la solicitud real después de recibir permiso.
Dado que el navegador envía solicitudes preflight con Method::OPTIONS, es necesario manejar dichas solicitudes. El middleware CORS debe agregarse al Service.
Uso de CORS en Salvo
Salvo proporciona middleware CORS integrado que puede configurarse y usarse fácilmente. Aquí hay un fragmento de código de ejemplo:
Warning
Nota: El middleware .hoop(cors) se aplica al Service, no al Router. Esto garantiza el manejo automático de las solicitudes preflight 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();
// Configurar el router del backend con protección CORS
let router = Router::with_path("hello").post(hello);
let service = Service::new(router).hoop(cors);
Código de Ejemplo
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
Opciones Principales de Configuración
El middleware CORS proporciona varias opciones de configuración:
- Orígenes Permitidos: Controla qué dominios pueden acceder a los recursos.
- Métodos Permitidos: Especifica los métodos HTTP permitidos (GET, POST, etc.).
- Encabezados Permitidos: Especifica los encabezados de solicitud permitidos.
- Encabezados Expuestos: Especifica qué encabezados de respuesta pueden ser accedidos por el cliente.
- Permitir Credenciales: Determina si las solicitudes pueden incluir credenciales como cookies.
- Tiempo de Caché de Solicitud Preflight: La duración del caché para los resultados de las solicitudes preflight.
Al configurar CORS adecuadamente, puedes garantizar tanto la seguridad como la capacidad de cumplir con los requisitos de acceso de origen cruzado.