常用的解决跨域的方法

972 阅读1分钟

跨域

同源限制

  • 两个网页中协议,域名和端口号都相同则是同一个源;如果有一个不同则是不同源。
  • 例如,www.example.com/api/data 这个网址,它的协议是http://,域名是www.example.com ,端口是80;http协议默认端口是80,https默认端口是443
http://www.example.com/api/query     同源
http://example.com/api/data          不同源(域名不同)
http://www.example.com:300/api/data  不同源(端口不同)
https://www.example.com/api/data     不同源(协议不同)
  • 限制范围
1. 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
2. 无法接触非同源网页的 DOM
3. 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)

跨域

  • 跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。下面是端口号不同的跨域

QQ截图20210614181254.png

  • 有三个标签是允许跨域加载资源
<img src="xxx">
<link href="xxx"></link>
<script src="xxx"></script>  ==>  JSONP解决跨域

跨域常用解决方案

JSONP方式

  • 仅支持GET请求,本质上就是插入一个script标签
  • 使用JSONP解决跨域需要引入jQuery,需要前后端一起才能解决跨域
  • 前端代码
<body>
  <button onclick="getJsonp()">jsonp解决跨域</button>
  <script>
    function getJsonp() {
      $.ajax({
        url: 'http://localhost:3000/api/jsonp',//请求地址
        data: {name: 'abc'},//get请求中的查询条件
        dataType: 'jsonp',//类型
        jsonpCallback: 'foo'//请求成功后执行的回调函数名
      });
      ==>等同于
      let script = document.createElement('script');
      //script标签的src属性是下图中的URL地址
      script.setAttribute('src', 'http://localhost:3000/api/jsonp?callback=foo&name=abc&_=1623667312766');
      document.body.appendChild(script);
    }
   
    function foo(data) {
      console.log(data);
    }
  </script>
</body>
  • JSONP请求发送的地址等同于下图的地址,该地址中有一个callback回调函数,后端可以通过req.query.callback获取回调函数名

2.png

  • 后端代码
app.get('/api/jsonp', (req, res) => {
  let data = JSON.stringify({name: 'json数据'});//请求成功后返回的数据
  let callback = `${req.query.callback}(${data})`;//回调函数foo(data)的字符串形式
  res.send(callback);
});

CORS方式

  • CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制
let cors = function(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', '*');//接收任意域名的请求
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');  //允许任何方法
  res.setHeader('Access-Control-Allow-Headers', '*');   //允许任何类型的头字段
  next(); //下一步
};
app.use(cors);//运用跨域的中间件
app.put('/api/getData', (req, res) => {
  res.send('hello');
});
简单请求
  • 只要同时满足以下两大条件,就属于简单请求
  1. 请求方法是以下三种方法之一
HEAD
GET
POST
  1. HTTP 的头信息不超出以下几种字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
非简单请求
  • 非简单请求是那种对服务器提出特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
  • 非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(预请求OPTIONS)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。这是为了防止这些新增的请求,对传统的没有 CORS 支持的服务器形成压力,给服务器一个提前拒绝的机会,这样可以防止服务器收到大量DELETE和PUT请求。

3.png

Nginx方式

  • 需要将你的index.html页面放到nginx中的html文件中
  • 配置nginx中的conf目录中的nginx.conf文件
//一个服务
server {
  listen	3000;//端口号3000
  server_name	localhost;//域名,本机就是localhost
  //跨域
  add_header Access-Control-Allow-Origin $http_origin always;
  add_header Access-Control-Allow-Credentials true always;
  add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;
  add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep- Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content- Type,Authorization' always;
  //匹配到根目录/时(这里是localhost:3000)会转发到index.html或index.htm
  location / {
    root    html;//根目录 ==> html目录
    index   index.html index.htm;
  }
  //匹配到以api/getData开头的请求(这里是我发送的请求)会转发到http://localhost:3000/api/getData
  location /api/getData {
    proxy_pass http://localhost:3000/api/getData;
  }