1. O que é domínio cruzado
Domínio cruzado significa que os navegadores não podem executar scripts de outros sites. É causado pela política de mesma origem do navegador, que é uma restrição de segurança imposta pelo navegador no JavaScript. Quando um navegador solicita recursos de outro nome de domínio de uma página da Web de um nome de domínio, qualquer diferença no nome de domínio, porta ou protocolo é considerada entre domínios.
A política de mesma origem restringe os seguintes comportamentos:
Cookies, LocalStorage e IndexDB não podem ser lidos
Objetos DOM e JS não podem ser buscados
A solicitação Ajax não pode ser enviada
Aqui está um exemplo:
http://www.yyy.cn/index.html 调用 http://www.xxxyyy.cn/server.php 非跨域
http://**www.xxxyyy.cn**/index.html 调用 http://**www.xxx.cn**/server.php 跨域,主域不同
http://**abc**.xxxyyy.cn/index.html 调用 http://**def**.xxx.cn/server.php 跨域,子域名不同
http://www.xxx.cn:**8080**/index.html 调用 http://www.xxx.cn/server.php 跨域,端口不同
**https**://www.xxx.cn/index.html 调用 **http**://www.xxx.cn/server.php 跨域,协议不同
2. CORS de compartilhamento de recursos entre origens
Esta é a solução mainstream atual.
CORS
É um padrão W3C, o nome completo é "compartilhamento de recursos entre domínios" ( Cross-origin resource sharing
). Ele permite que os navegadores enviem XMLHttpRequest
solicitações para servidores de origem cruzada, superando assim a limitação de que o AJAX só pode ser usado na mesma origem.
CORS
Tanto o navegador quanto o suporte ao servidor são necessários. No momento, todos os navegadores suportam esta função, e o navegador IE não pode ser inferior à IE10。IE8+:IE8/9
necessidade de usar XDomainRequest
objetos para apoiá-lo CORS
.
Todo o CORS
processo de comunicação é concluído automaticamente pelo navegador sem a participação do usuário. Para desenvolvedores, CORS
a comunicação não é diferente da comunicação AJAX de mesma origem e o código é exatamente o mesmo. Assim que o navegador descobrir que AJAX
a solicitação é de origem cruzada, ele adicionará automaticamente algumas informações de cabeçalho adicionais e, às vezes, haverá uma solicitação adicional, mas o usuário não a sentirá. Portanto, CORS
a chave para realizar a comunicação é o servidor.
Basicamente, você só precisa adulterar o servidor, e o código do front-end é o mesmo de quando é da mesma fonte, ou seja, é o mesmo quando não é cross-domain.
Esse método é dividido em dois tipos de solicitações:
uma é uma solicitação simples e a outra é uma solicitação não simples. Desde que as seguintes condições sejam atendidas, é uma solicitação simples e o método de solicitação é que HEAD、POST 或者 GET,http
as informações do cabeçalho não excedam os seguintes campos:
Accept、Accept-Language
Content-Language
Last-Event-ID
Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
为什么要分为简单请求和非简单请求,因为浏览器对这两种请求方式的处理方式是不同的。
2.1 Pedido Simples
Para solicitações simples, o navegador faz CORS
a solicitação diretamente. Especificamente, é adicionar um campo nas informações do cabeçalho Origin
. Veja a seguir um exemplo: quando o navegador descobre que essa AJAX
solicitação de origem cruzada é uma solicitação simples, ele adiciona automaticamente um campo às informações do cabeçalho Origin
.
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...
Origin
O campo é usado para indicar de qual fonte (protocolo + nome de domínio + porta) esta solicitação vem. Com base nesse valor, o servidor decide se concorda com a solicitação.
Se Origin
a fonte especificada não estiver dentro do intervalo permitido, o servidor retornará uma HTTP
resposta normal. Quando o navegador descobre que as informações do cabeçalho de resposta não contêm Access-Control-Allow-Origin
campos, ele sabe que algo deu errado e, portanto, lança um erro, que é capturado pela função XMLHttpRequest
de retorno de chamada .onerror
Observe que esse tipo de erro não pode ser identificado pelo código de status, porque o código de status da resposta HTTP pode ser 200.
Se o nome de domínio especificado pelo Origin estiver dentro do intervalo permitido, a resposta retornada pelo servidor terá vários outros campos de cabeçalho:
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
Entre as informações do cabeçalho acima, há três campos relacionados a solicitações CORS, todos começando com Access-Control-
**Access-Control-Allow-Origin *
: Este campo é obrigatório. Seu valor é o valor do campo Origem no momento da solicitação, ou um, indicando que as solicitações de qualquer nome de domínio são aceitas
Access-Control-Allow-Credentials
: Este campo é opcional. Seu valor é um booleano que indica se os cookies podem ser enviados. Por padrão, os cookies não são incluídos nas solicitações CORS. Se estiver definido como true, significa que o servidor permite expressamente que o Cookie possa ser incluído na solicitação e enviado ao servidor em conjunto. Este valor só pode ser definido como verdadeiro.Se o servidor não quiser que o navegador envie cookies, basta excluir este campo.
Access-Control-Expose-Headers
: Este campo é opcional. CORS
Ao solicitar, o método XMLHttpRequest
do objeto getResponseHeader()
pode obter apenas 6 campos básicos: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
. Se você deseja obter outros campos, deve Access-Control-Expose-Headers
especificá-los nele.
atributo withCredentials
Conforme mencionado acima, as solicitações CORS não enviam cookies e informações de autenticação HTTP por padrão. Se você deseja enviar cookies para o servidor, por um lado, você precisa que o servidor concorde em especificar o campo Access-Control-Allow-Credentials.
Por outro lado, os desenvolvedores devem ativar o atributo withCredentials em solicitações AJAX.
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
// jquery
$.ajax({
...
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
Caso contrário, mesmo que o servidor concorde em enviar o cookie, o navegador não o enviará. Ou o servidor solicita uma configuração Cookie
e o navegador não lida com isso. No entanto, se a configuração withCredentials for omitida, alguns navegadores ainda o enviarão juntos Cookie
. Nesse caso, ele pode ser explicitamente fechado withCredentials
.
* Deve-se observar que, se você deseja enviá-lo, Cookie,Access-Control-Allow-Origin
não pode defini-lo como um asterisco e deve especificar um nome de domínio claro que seja consistente com a página da Web solicitada. **Ao mesmo tempo, Cookie
a política de mesma origem ainda é seguida, apenas aqueles definidos com o nome de domínio do servidor Cookie
serão carregados, outros nomes de domínio Cookie
não serão carregados e os códigos originais da página da Web (origem cruzada) document.cookie
não podem ser lidos em o nome de domínio do servidor Cookie
.
2.2 Pedidos não simples
Uma solicitação não simples é uma solicitação que possui requisitos especiais para o servidor, como o método de solicitação é PUT
ou DELETE
, ou Content-Type
o tipo de campo é application/json
.
Para solicitações que não sejam simples , CORS
será adicionada HTTP
uma solicitação de consulta antes da comunicação formal, que é chamada de solicitação de "pré-verificação" preflight
( ) - o navegador primeiro pergunta ao servidor se o nome de domínio da página da Web atual está no lista de permissões do servidor e se ele pode Quais HTTP
verbos e campos de cabeçalho usar. Somente quando uma resposta positiva for recebida, o navegador emitirá uma XMLHttpRequest
solicitação formal, caso contrário, um erro será relatado.
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
Quando o navegador descobre que esta é uma solicitação não simples, ele envia automaticamente uma solicitação "preflight", solicitando ao servidor que confirme se tal solicitação é possível. Abaixo estão os cabeçalhos HTTP para esta solicitação "preflight".
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
O método de solicitação usado pela solicitação "preflight" é OPTIONS
para indicar que essa solicitação é para consulta. Nas informações do cabeçalho, o campo chave serve Origin
para indicar de qual origem vem a requisição. Além dos Origin
campos, o cabeçalho da solicitação "preflight" contém dois campos especiais.
Access-Control-Request-Method
: Este campo é obrigatório e serve para listar quais métodos HTTP serão usados pela requisição CORS do navegador. O exemplo acima é PUT.
Access-Control-Request-Headers
: Este campo é uma string separada por vírgula, especificando os campos de informações de cabeçalho adicionais que o navegador enviará em solicitações CORS. O exemplo acima é X-Custom-Header
Resposta à solicitação de simulação
Depois que o servidor recebe a solicitação "preflight", verifica Origin、Access-Control-Request-Method和Access-Control-Request-Headers
os campos, confirma se as solicitações de origem cruzada são permitidas e, em seguida, responde
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
Na resposta HTTP acima, a chave é Access-Control-Allow-Origin
o campo, indicando que http://api.bob.com
os dados podem ser solicitados. Este campo também pode ser definido como um asterisco, que concorda com qualquer solicitação de origem cruzada.
Se o navegador negar a solicitação "preflight", uma resposta HTTP normal será retornada, mas sem nenhum campo de cabeçalho relacionado ao CORS. Neste momento, o navegador determinará que o servidor não concorda com a solicitação de simulação, então um erro é acionado, o qual é capturado pela função onerror callback do objeto XMLHttpRequest. O console imprimirá uma mensagem de erro.
Outros campos relacionados ao CORS respondidos pelo servidor são os seguintes:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Access-Control-Allow-Methods
: Este campo é obrigatório e seu valor é uma string separada por vírgulas, indicando todos os métodos de solicitação entre domínios suportados pelo servidor. Observe que todos os métodos suportados são retornados, não apenas aquele solicitado pelo navegador. Isso é para evitar várias solicitações "preflight".Access-Control-Allow-Headers
: O campo é obrigatório se a solicitação do navegadorAccess-Control-Request-Headers
o incluir.Access-Control-Allow-Headers
É também uma string separada por vírgula indicando todos os campos de cabeçalho suportados pelo servidor, não limitados aos solicitados pelo navegador em "preflight".Access-Control-Allow-Credentials
: Este campo tem o mesmo significado da solicitação simples.Access-Control-Max-Age
: Este campo é opcional e é usado para especificar o período de validade desta solicitação de simulação, em segundos. No resultado acima, o período de validade é de 20 dias (1.728.000 segundos), o que significa que a resposta pode ser armazenada em cache por 1.728.000 segundos (20 dias), período durante o qual outra solicitação de simulação não precisa ser enviada.
O navegador responde normalmente ao pedido
Depois que o servidor passar pela solicitação de "pré-verificação", todas as solicitações CORS normais do navegador serão iguais a uma solicitação simples e haverá um campo de cabeçalho Origin. A resposta do servidor também terá um Access-Control-Allow-Origin
campo de cabeçalho.
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Uma solicitação CORS normal do navegador. O campo Origem das informações do cabeçalho acima é adicionado automaticamente pelo navegador. Abaixo está a resposta normal do servidor.
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin
campo é obrigatório para cada resposta
3. Processamento entre domínios do Nodejs
3.1 A configuração permite nomes de domínio entre domínios
- Defina para permitir todos os nomes de domínio entre domínios
var express = require('express');
var app = express();
// 设置允许所有域名跨域:
app.all("*",function(req,res,next){
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin","*");
//允许的header类型
res.header("Access-Control-Allow-Headers","content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //让options尝试请求快速结束
else
next();
})
- Defina para permitir vários nomes de domínio entre domínios:
app.all("*",function(req,res,next){
var orginList=[
"http://www.bibi.com",
"http://www.qq.com",
"http://www.baidu.com"
]
// 防止undefined 报错
if(!req.headers.origin){
return
}
if(orginList.includes(req.headers.origin.toLowerCase())){
//允许的header类型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin",req.headers.origin);
if (req.method.toLowerCase() == 'options'){
res.sendStatus(200); //让options尝试请求快速结束
}
else{
next();
}
} else {
res.sendStatus(500)
}
})
3.2 Usando núcleos de middleware
Instalarnpm i cors -S
Uso 1: Habilitar cross-domain para todas as fontes
const cors = require('cors')
app.use(cors())
app.listen(8000, function () {
console.log('start')
})
Método de uso 2: configurar por condição
var express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['http://example1.com', 'http://example2.com']
// 异步配置
var corsOptions;
var corsOptionsDelegate = function (req, callback) {
if (whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = {
origin: true} //在CORS响应中反映(启用)请求的起源
} else {
corsOptions = {
origin: false} // 拦截请求
}
callback(null, corsOptions) // error options
}
app.all("*", cors(corsOptionsDelegate), function (req, res, next) {
if(corsOptions.origin === true){
if (req.method.toLowerCase() == 'options'){
res.sendStatus(200); //让options尝试请求快速结束
}
else{
next();
}
} else {
res.sendStatus(500); //被拦截
}
})
app.post('/cors',(req,res)=> {
res.send('ok')
})
app.listen(8000, function () {
console.log('start')
})