跨域请求

什么是跨域请求?

当浏览器执行一个脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域

什么是同源?

同源即:由Netscape提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源(origin)中加载文本或者脚本与来自其他源(origin)中资源的交互方式
,所谓的同源就是指协议、域名、端口相同。

只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

协议?域名?端口?

协议:网络协议遍及OSI通信模型(OSI七层模型,常用协议有TCP/IP、HTTP、FTP协议等)
域名:Domain Name,网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)
端口:是设备与外界通讯交流的出口,分为物理端口和虚拟端口(常见的如80端口)

了解概念后我们知道跨域请求就是web浏览器自身不允许在域名、协议、端口等都不相同的情况下进行页面请求方为,因此作为前端developer在项目开发时是需要解决此类问题的!

为什么要有这种限制?非同源请求页面会怎么样?

设想这样一个情景:A网站是一家银行,用户登录以后,又去浏览其他的网站B,如果网站B可以读取A网站的Cookie,会发生什么问题?
显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄露,更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒用户,为所欲为。因为浏览器同时还规定,提交表单不受同源策略的限制。

非同源限制范围

  • Cookie、LocalStorage和IndexDB无法获取
  • DOM无法获得
  • AJAX请求不能发送

跨域请求解决方案

1. jsonp:(JSON with Padding是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题)
原理:网页客户端动态添加<script>标签添加src属性,向服务端发送json请求(不受同源策略束缚)服务器收到请求后,将数据放在一个指定名字的回调函数里(作为参数)传回来。

    // 前端请求代码
    //http://127.0.0.1:8888/jsonp.html
var script = document.createElement('script');
      script.src = 'http://127.0.0.1:2333/jsonpHandler?callback=_callback'
      document.body.appendChild(script);      //插入script标签
      //回调处理函数 _callback
      var _callback = function(obj){
          for(key in obj){
            console.log('key: ' + key +' value: ' + obj[key]);
          }
      }
// 后端响应代码
//http://127.0.0.1:2333/jsonpHandler
app.get('/jsonpHandler', (req,res) => {
  let callback = req.query.callback;
  let obj = {
    type : 'jsonp',
    name : 'weapon-x'
  };
  res.writeHead(200, {"Content-Type": "text/javascript"});
  res.end(callback + '(' + JSON.stringify(obj) + ')');
})

JSONP只支持Get请求方式

2.CORS:(跨域资源共享)是由W3C制定的跨站资源分享标准,可以让AJAX实现跨域访问,除了 GET 要求方法以外也支持其他的 HTTP 要求。服务器一般需要增加如下响应头的一种或几种:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

跨域请求默认不会携带Cookie信息,如果需要携带,请配置下述参数:

"Access-Control-Allow-Credentials": true
// Ajax设置
"withCredentials": true

3.window.name+iframe:利用iframe标签的跨域能力,window.name属性值在文档刷新后依旧存在的能力(且最大允许2M左右)

<!-- 
 下述用端口 
 10000表示:domainA
 10001表示:domainB
-->

<!-- localhost:10000 -->
<script>
  var iframe = document.createElement('iframe');
  iframe.style.display = 'none'; // 隐藏

  var state = 0; // 防止页面无限刷新
  iframe.onload = function() {
      if(state === 1) {
          console.log(JSON.parse(iframe.contentWindow.name));
          // 清除创建的iframe
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
          document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          // 加载完成,指向当前域,防止错误(proxy.html为空白页面)
          // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame.
          iframe.contentWindow.location = 'http://localhost:10000/proxy.html';
      }
  };

  iframe.src = 'http://localhost:10001';
  document.body.appendChild(iframe);
</script>

<!-- localhost:10001 -->
<!DOCTYPE html>
...
<script>
  window.name = JSON.stringify({a: 1, b: 2});
</script>
</html>

4.window.name:window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

<script> //a页面设置
window.name="kowalski";
</script>
<script> //b页面取出
alert(window.name);
</script>


正在接触后续补充…

    原文作者:kowalski
    原文地址: https://segmentfault.com/a/1190000013985405
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞