CORS全称"跨域资源共享"(Cross-origin resource sharing)。
跨域资源共享是一份浏览器技术的规范,以避开浏览器的同源策略
CORS约定服务器端和浏览器在HTTP协议之上,通过一些额外HTTP头部信息,进行跨域资源共享的协商。服务器端和浏览器都必需遵循规范中的要求。
XMLHttpRequest支持通过withCredentials属性实现在跨域请求携带身份信息(Credential,例如Cookie或者HTTP认证信息)。浏览器将携带Cookie Header的请求发送到服务器端后,如果服务器没有响应Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。
Ajax XMLHttpRequest对象发起请求,所有的CORS HTTP请求头都可以由浏览器填充,无需在XMLHttpRequest对象中设置。
下面是CORS协议规定的HTTP头,用来进行浏览器发起跨域资源请求时进行协商:
Origin
。HTTP请求头,任何涉及CORS的请求都必需携带。Access-Control-Request-Methods
。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的方法。Access-Control-Request-Headers
。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的自定义Header列表。Access-Control-Allow-Origin
。HTTP响应头,指定服务器端允许进行跨域资源访问的来源域。可以用通配符*表示允许任何域的JavaScript访问资源,但是在响应一个携带身份信息(Credential)的HTTP请求时,Access-Control-Allow-Origin必需指定具体的域,不能用通配符。Access-Control-Allow-Methods
。HTTP响应头,指定服务器允许进行跨域资源访问的请求方法列表,一般用在响应预检请求上。Access-Control-Allow-Headers
。HTTP响应头,指定服务器允许进行跨域资源访问的请求头列表,一般用在响应预检请求上。Access-Control-Max-Age
。HTTP响应头,用在响应预检请求上,表示本次预检响应的有效时间。在此时间内,浏览器都可以根据此次协商结果决定是否有必要直接发送真实请求,而无需再次发送预检请求。Access-Control-Allow-Credentials
。HTTP响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回Access-Control-Allow-Credentials: true的,浏览器都会忽略此次响应。模拟服务端口为3000的客户端
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3000端口title>
head>
<body>
<button class="btn1">非跨域(同源)请求button>
<button class="btn2">跨域(非同源)请求button>
<script>
// 非跨域请求
//当我们通过XMLHttpRequest对象向端口为3000的服务端发起请求时,请求成功并获取字符串数据:"同源访问3000端口"
document.querySelector(".btn1").onclick = function(){
let xhr = new XMLHttpRequest();
xhr.open("post","/post",true);
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.send();
}
// 跨域请求(说请求成功了,但没拿到数据)
document.querySelector(".btn2").onclick = function(){
let xhr = new XMLHttpRequest();
// 跨域请求时,请求url不能是相对路径了,写法如下:
xhr.open("post","http://localhost:4000/post",true);
xhr.onload = function(){
console.log(xhr.responseText);
// 获取返回头部信息
let res = xhr.getAllResponseHeaders();
console.log(res);
}
xhr.send();
}
script>
body>
html>
模拟服务端口为3000的服务端
indexOne.js
// 服务端口在3000
const Koa = require("koa");
const static = require("koa-static");
const Router = require("koa-router");
let app = new Koa();
let router = new Router();
app.use(static(__dirname+"/static"));
router.get("/",ctx=>{
ctx.body = "3000端口";
});
router.post("/post",ctx=>{
ctx.body = "同源访问3000端口";
});
app.use(router.routes());
app.listen(3000);
模拟服务端口为4000的服务端
indexTwo.js
// 服务端口在4000
const Koa = require("koa");
const static = require("koa-static");
const Router = require("koa-router");
let app = new Koa();
let router = new Router();
app.use(static(__dirname+"/static"));
router.get("/",ctx=>{
ctx.body = "4000端口";
})
router.post("/post",ctx=>{
ctx.body = "非同源访问4000端口";
});
app.use(router.routes());
app.listen(4000);
我们直接运行上面代码后,我们看到,同源的客户端访问服务端,成功了并拿到了数据。而非同源的客户端访问不同端口的服务端(即服务端口为3000的客户端直接去访问端口为4000服务器)就报错了,并没有拿到数据,这就涉及到了跨域问题!
"同源策略"是浏览器规定的,所以我们对上面的代码改动一下,在端口为4000服务端对cors设置http头部进行相关设置。
router.post("/post",ctx=>{
ctx.body = "非同源访问4000端口";
// 对http头部信息进行相应的设置
// 允许跨域访问(表明来自哪里的跨域请求),必须设置
// (1)"*"号表示允许任何域向我们的服务端提交请求:
// ctx.set("Access-Control-Allow-Origin:*")
// (2)在响应一个携带身份信息(Credential)的HTTP请求时,
// 必需指定具体的域名,不能用通配符"*"。如下:
// 允许服务端口为3000的站点进行跨域访问
ctx.set("Access-Control-Allow-Origin","http://localhost:3000");
// 允许获取头部信息
ctx.set("Access-Control-Expose-Headers","Content-Type,X-Requested-With,token");
//设置允许前端发送的请求方式(CORS支持多种请求方式)
ctx.set("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");
});
如下图,跨域请求成功,并获取了字符串数据"非同源访问4000端口"
如下图,跨域访问成功后,从控制台可以查看到Response Headers下,一些http请求的一些相应的头部信息