Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 431|回复: 0

了解前端跨域问题及解决方案(面试逼问)

[复制链接]
  • TA的每日心情
    奋斗
    昨天 16:00
  • 签到天数: 755 天

    [LV.10]以坛为家III

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    707348
    发表于 2021-4-20 14:57:25 | 显示全部楼层 |阅读模式

    1.什么是跨域?
    跨域是指一个域下的文档或脚本试图去请求另一个域下的资源(广义),但通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

    2.什么是同源策略?
    同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同。

    3.跨域解决方案
    (1)通过jsonp跨域
    原理:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。

    <script type="text/javascript"> 
      function jsonpCallback(res)   {     alert(res.msg);   } </script> 
    <script type="text/javascript"src="http://xxx.com/xx?jsonp=jsonpCallback"></script>

    一、原生实现:

    <script>
        var script = document.createElement('script'); script.type = 'text/javascript'; // 传参并指定回调执行函数为callBack
      script.src = 'http://xxxx.com?callback=callBack';   document.head.appendChild(script);// 回调执行函数
      function callBack(res) {     alert(JSON.stringify(res));   } </script>

    服务端返回如下(返回时即执行全局函数):
    callBack({"status": true, "xxx": "xxx"})

    二、jquery ajax:jq实现

    $.ajax({   url: 'http://xxxx.com',   type: 'get',   dataType: 'jsonp', //请求方式为jsonp
      jsonpCallback: "callBack",//自定义回调函数名
      data: {} });

    三、vue.js:vue实现

    this.$http.jsonp('http://xxx.com', {   params: {},   jsonp: 'callBack' }).then((res) => {   console.log(res); })

    JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
    JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

    2、 document.domain + iframe跨域(仅限主域相同,子域不同的跨域应用场景)
    实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域;

    (1)父窗口:(http://www.domain.com/a.html)
    <iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
    <script>   document.domain = 'domain.com';   var res= 'data'; </script>
    (2)子窗口:(http://child.domain.com/b.html)
    <script>   document.domain = 'domain.com';   // 获取父窗口中变量
      console.log('res' + window.parent.res); </script>

    3、 location.hash + iframe
    实现原理: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

    1.a.html:(http://www.domain1.com/a.html)
    <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
    <script>
      var iframe = document.getElementById('iframe');   //向b.html传hash值
      setTimeout(function() {     iframe.src = iframe.src + '#user=admin';   }, 1000);   //开放给同域c.html的回调方法
      function onCallback(res) {     alert('data from c.html ---> ' + res);   } </script>
    2.b.html:(http://www.domain2.com/b.html) <iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe> <script>   var iframe = document.getElementById('iframe');   //监听a.html传来的hash值,再传给c.html   window.onhashchange = function () {     iframe.src = iframe.src + location.hash;   }; </script>
    3.c.html:(http://www.domain1.com/c.html) <script>   //监听b.html传来的hash值   window.onhashchange = function () {     //再通过操作同域a.html的js回调,将结果传回     window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));   }; </script>

    4、 window.name + iframe跨域
    window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

    (1)a.html:(http://www.domain1.com/a.html)
    var proxy = function(url, callback) {   var state = 0;   var iframe = document.createElement('iframe');   // 加载跨域页面
      iframe.src = url;   // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
      iframe.onload = function() {     if (state === 1) {       // 第2次onload(同域proxy页)成功后,读取同域window.name中数据
          callback(iframe.contentWindow.name);       destoryFrame();     } else if (state === 0) {       // 第1次onload(跨域页)成功后,切换到同域代理页面
          iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';       state = 1;     }   };   document.body.appendChild(iframe);   // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
      function destoryFrame() {     iframe.contentWindow.document.write('');     iframe.contentWindow.close();     document.body.removeChild(iframe);   } }; // 请求跨域b页面数据
    proxy('http://www.domain2.com/b.html', function(data){   alert(data); }); (2)proxy.html:(http://www.domain1.com/proxy....
      中间代理页,与a.html同域,内容为空即可。 (3)b.html:(http://www.domain2.com/b.html)
    <script>   window.name = 'This is domain2 data!'; </script>

    总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

    5、 postMessage跨域
    postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
    a.) 页面和其打开的新窗口的数据传递
    b.) 多窗口之间消息传递
    c.) 页面与嵌套的iframe消息传递
    d.) 上面三个场景的跨域数据传递

    用法:postMessage(data,origin)方法接受两个参数
    data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
    origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

    (1)a.html:(http://www.domain1.com/a.html)
    <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
    <script> 
      var iframe = document.getElementById('iframe');   iframe.onload = function() {     var data = {       name: 'xxx'     };   // 向domain2传送跨域数据
      iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');   };   // 接受domain2返回数据
      window.addEventListener('message', function(e) {     alert('data from domain2 ---> ' + e.data);   }, false); </script>
    (2)b.html:(http://www.domain2.com/b.html)
    <script>
      // 接收domain1的数据
      window.addEventListener('message', function(e) {     alert('data from domain1 ---> ' + e.data);     var data = JSON.parse(e.data);     if (data) {       data.number = 16;       // 处理后再发回domain1
          window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');     }   }, false); </script>

    6、 跨域资源共享(CORS)
    普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

    需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。

    目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

    1、 前端设置:

    (1)原生ajax // 前端设置是否带cookie
    xhr.withCredentials = true; 示例代码: var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
    // 前端设置是否带cookie
    xhr.withCredentials = true; xhr.open('post', 'http://www.domain2.com:8080/login', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('canshu=xxx');
    xhr.onreadystatechange
    = function() {   if (xhr.readyState == 4 && xhr.status == 200) {     alert(xhr.responseText);   } };
    (2)jQuery ajax $.ajax({ ...   xhrFields: {     withCredentials: true // 前端设置是否带cookie
      },   crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
    ... });
    (3)vue框架 1/axios设置: axios.defaults.withCredentials = true 2/vue-resource设置: Vue.http.options.credentials = true

    7、 nginx代理跨域
    8、 nodejs中间件代理跨域
    9、 WebSocket协议跨域

    后面三种参考该博主 https://segmentfault.com/a/1190000011145364 的nodejs相关介绍。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-6-27 17:42 , Processed in 0.064449 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表