跨域资源共享

293 阅读3分钟

CORS

跨源资源共享(CORS,Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信 CORS背后的思路就是通过使用自定义HTTP头部来实现。

在进行跨域请求时,浏览器会把请求分为两种,简答请求和非简单请求,两种请求的处理方式不一样。

简单请求

简单请求是为了兼容表单,因为表单是可以跨域进行的。 满足以下条件即为简单请求。

  • 请求方法为GETPOSTHEAD方法
  • 请求头为AcceptAccept-LanguageContent-Type content-type只能为application/x-www-formurlencodedmultipart/form-datatext/plain,除了这三种,例如application/json即为非简单请求。

浏览器在发出简单请求时,会在HTTP头加一个Origin字段,表明请求的源(协议+域名+端口)。 服务器根据判断该源是否在许可范围内。

如果该源不在许可范围内,则服务器正常响应。但浏览器在响应中找不到Access-Control-Allow-Origin这个响应头,则会抛出错误,被xhrerror事件处理程序捕捉。

如果该源在许可范围内,则会在响应头部加入Access-Control-Allow-Origin请求源或 *,

其它响应头:

  • Access-Control-Allow-Headers,表示可以在请求头出现的字段。
  • Access-Control-Allow-Methods,表示可以请求的方法
  • Access-Control-Allow-Credentials,表示是否可以携带Cookie
如果一个请求需要携带Cookie,假如是通过XMLHTTPRequest对象来发出
那么客户端要设置xhr.withCredentials为true,这个一般默认为true,可以设置为false关闭,不发送Cookie  
服务端要设置Access-Control-Allow-Credentials为true

非简单请求

除了简单请求,那就是非简单请求了。 非简单请求之前,浏览器会先发送一个预检请求,根据响应决定是否发送请求。

预检请求

预检请求是一个option请求,主要增加3个头信息

  • Origin表明源
  • Access-Control-Request-Method,表示请求的方法
  • Access-Control-Request-Headers,表示请求要用到的头部

image.png

服务器看看请求的方法其它请求头是否在自己的许可范围内。如果是,那么就返回相对应的字段。

image.png 如果服务器不支持该跨域请求,就返回正常的HTTP响应,即不带Access-Control-Allow-**类似的字段,浏览器看到了会自动拦截报错的。

  • Access-Control-Allow-Max-Age貌似是缓存预检请求响应,单位为秒,但是我试了好像并没有什么效果。

如果预检请求通过后,就正常的发起请求,多加一个Origin字段。
如果预检请求没通过,就没有后续了,不会再发东西。
响应需要一直携带Access-Control-Allow-xx响应头,否则通过了预检请求也不好使。后面的请求也会被浏览器所拦截


JSONP

JSONP是利用<script>标签不受跨域的影响,从而可以获取资源,仅限GET.
前端代码

function JSONP(src, callback, id) {
  let script = document.createElement("script");
  script.src = src + "?callback=" + callback + "&id=" + id;
  document.body.append(script);
}

function getPerson(res) {
  console.log(res);
}

JSONP("http://localhost:3000", "getPerson", 1);

服务端代码

let person = [
  { id: 1, name: "zs", age: 22 },
  { id: 2, name: "ls", age: 20 },
];

server.get("/", (req, res) => {
  let cb = req.query.callback
  let id = req.query.id || 0
  let someone = person.find(ele => ele.id == id) || "查无此人"
  res.send(`${cb}(${JSON.stringify(someone)})`);
});

服务端返回一个字符串,前端请求回来当作JS代码执行,执行事先定义好的方法,服务端把前端需要的值放在方法的参数中

Content-Type

application/x-www-formurlencodedmultipart/form-data都是表单发送编码格式,有啥区别

application/x-www-formurlencoded把键值对组成name=zs&age=22这种表单格式发送。

multipart/form-data则是通过boundary分割

image.png