Controlo de Origem Cruzada
CORS (Cross-Origin Resource Sharing) é um mecanismo que permite aos navegadores fazer pedidos a servidores de origem cruzada, superando assim as restrições impostas pela política de mesma origem do navegador.
O que é a Política de Mesma Origem?
A política de mesma origem é uma funcionalidade de segurança dos navegadores que restringe como documentos ou scripts carregados de uma origem podem interagir com recursos de outra origem. "Mesma origem" refere-se a ter o mesmo protocolo, domínio e número de porta.
Por que é Necessário o CORS?
O suporte a CORS é necessário quando uma aplicação frontend precisa aceder a APIs de uma origem diferente. Por exemplo:
- A aplicação frontend é implementada em
https://frontend.com
- O serviço API é implementado em
https://api.backend.com
Sem CORS, o navegador impedirá a aplicação frontend de aceder ao serviço API.
Como Funciona o CORS
O CORS implementa o controlo de acesso de origem cruzada através de uma série de cabeçalhos HTTP:
- Pedidos Simples: Enviados diretamente, com o servidor a controlar se o acesso é permitido através dos cabeçalhos de resposta.
- Pedidos de Pré-Voo (Preflight): O navegador envia primeiro um pedido OPTIONS para perguntar ao servidor se o acesso de origem cruzada é permitido, e só envia o pedido real após receber permissão.
Como o navegador envia pedidos de pré-voo com Method::OPTIONS, é necessário lidar com tais pedidos. O middleware CORS deve ser adicionado ao Service.
Usando CORS no Salvo
O Salvo fornece middleware CORS integrado que pode ser facilmente configurado e utilizado. Eis um exemplo de código:
Warning
Nota: O middleware .hoop(cors) é aplicado ao Service, não ao Router. Isto garante o tratamento automático dos pedidos de pré-voo 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 router backend com proteção CORS
let router = Router::with_path("hello").post(hello);
let service = Service::new(router).hoop(cors);
Código de Exemplo
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
Principais Opções de Configuração
O middleware CORS fornece várias opções de configuração:
- Origens Permitidas: Controla quais domínios podem aceder aos recursos.
- Métodos Permitidos: Especifica os métodos HTTP permitidos (GET, POST, etc.).
- Cabeçalhos Permitidos: Especifica os cabeçalhos de pedido permitidos.
- Cabeçalhos Expostos: Especifica quais cabeçalhos de resposta podem ser acedidos pelo cliente.
- Permitir Credenciais: Determina se os pedidos podem incluir credenciais como cookies.
- Tempo de Cache de Pedidos de Pré-Voo: A duração da cache para os resultados dos pedidos de pré-voo.
Ao configurar o CORS adequadamente, pode garantir tanto a segurança como a capacidade de satisfazer os requisitos de acesso de origem cruzada.