JS中的跨域请求

178 阅读5分钟

说到跨域请求, 一个绕不开的话题就是同源策略;(同协议+同域名+同端口)视为同源。它是浏览器的一个安全功能,保护用户数据(CookieStorageindexDB)被恶意获取; 阻止浏览器的脚本去请求非同源下的资源;阻止获取DOM。下面说的主要是跨域请求的常用解决办法。

JSONP

浏览器中html标签script可以通过src属性从不同域名下加载资源,不受同源策略的影响。可以基于此解决跨域请求; 通过动态创建script标签, 设置scriptsrc属性,然后通过document.head.append将其挂载到页面。当页面执行到这<script>这行代码的时候, 会向服务端去加载src所指向的资源,服务器收到请求后会将数据放入指定的回调函数中传回,资源请求成功后浏览器会自动执行js代码。

//客户端代码
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://localhost:8888/api/user?callback='+handleCallback;
document.head.appendChild(script);
function handleCallback(res) {
  alert(JSON.stringify(res));
}

//服务端代码
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
  const response = {code: 0, data: {name: "express"}}
  //注意这里需要是一个自执行函数,才能在返回的时候,自动执行这个函数
  var str = `(${req.query.callback})(${JSON.stringify(response)})`;
  res.end(str)
})
app.listen(8888)

服务器端设置代理

跨域只是出现在浏览器与服务器,并不会出现在服务器与服务器的通信中。通常我们的前端文件与后端的接口不在同一个服务中, 我们可以在前端的服务器中通过代理的方式,将本来在浏览器中发起的请求转发到代理服务器,由代理服务器向接口服务器发起请求,再由代理服务器将接口响应返回到浏览器。

CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器发起XMLHttpRequest请求。

//客户端 使用ajax模拟跨域调用
$.get("http://localhost:8888/api/user", res => {
  console.log(res)
})

//模拟服务端接口
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
  res.json({code: 0, data: {name: "express"}})
})
app.listen(8888)

image-20200709110901751

如果可以在服务端设置响应头 Access-Control-Allow-Origin* 就可以在客户端获取到数据

//模拟服务端接口
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
  res.header(" Access-Control-Allow-Origin", "*"//允许请求
  res.json({code: 0, data: {name: "express"}})
})
app.listen(8888)

浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求(options请求)

浏览器将CORS的跨域请求分为两类一类简单请求, 一类非简单请求

只有同时满足以下两条件才能算是简单请求

1.请求方式必须是GET、POST、HEAD三者之一

2.请求头字段信息是以下几种

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type(他的值仅限于三个

    ​ application/x-www-form-urlencoded,

    ​ multipart/form-data

    ​ text/plain

当浏览器发现这是一个简单的跨域请求,会自动在请求头中添加一个origin属性,origin头部字端用于说明本次请求的源(协议+域名+端口)。服务器就是依据此字端来跟Access-Control-Origin-Allow的值比较。决定是否响应该请求。如果origin指定的源不在服务器许可范围内,XMLHttpRequestonerror就会监听到错误,此时不能根据状态码判断

如果origin指定的源在服务器许可范围内,服务器返回的响应会多出几个响应头字段

Access-Control-Allow-Origin: 这个字段是必须的,要么是请求头中的origin值,要么就是*

Access-Control-Allow-Credentials: 这是一个可选字段,表示服务器许可cookie可以包含在请求头中发送, 但是还需要浏览器也同时设置WithCredentials为true, 浏览器默认不携带如果服务器想禁止浏览器发送cookie, 直接删除该字端就好。

**Access-Control-Expose-Headers: **这也是一个可选字段, 通常我们通过response.header只能获取到Expires、Cache-Control、Last-Modified、Content-Type, Content-Language、Pragma这6个字段,如果想要获取其他字段需要在此指定才能获取的到。

对于非简单请求, 例如使用了自定义的header字段, 或者Content-Type设置为application/json等, 浏览器都会自动先发送一个OPTIONS的预检请求, 询问服务器是否可以这样请求(当前网页的域名是否在服务器的许可名单中、是否有自定义的请求头字端、请求方法是否是post、get、head三者之一, content-type值是否符合类型)。预检请求会在请求头中自动添加两个请求头字段

Access-Control-Request-Method 指明请求方法

Access-Control-Request-Headers 该值是一个逗号分隔的字符串, 那些自定义字段

只有在得到肯定的答复后,浏览器才会发起正式的请求。

参考文章

  1. 浏览器同源政策及其规避方法
  2. 跨域资源共享 CORS 详解