美文网首页
js__JSONP__跨域

js__JSONP__跨域

作者: 好奇而已 | 来源:发表于2017-05-11 23:22 被阅读56次

    1: 什么是同源策略

    • 最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源",所谓"同源"指的是"三个相同".
      协议相同
      域名相同
      端口相同

    • 举例来说:
      http://www.example.com/dir/page.html
      这个网址协议是http://,域名是www.example.com,端口是80(默认端口可以省略),它的同源情况如下.
      http://www.example.com/dir2/other.html:同源
      http://example.com/dir/other.html:不同源(域名不同)
      http://v2.www.example.com/dir/other.html:不同源(域名不同)
      http://www.example.com:81/dir/other.html:不同源(端口不同)
      https://www.example.com:81/dir/other.html:不同源(https协议不同)

    2: 什么是跨域?跨域有几种实现形式

    • 跨域出现的原因
      JavaScript出于安全方面的考虑,不允许一个网页访问一个非同源的网页,即2个网址的协议相同,域名相同,端口相同其中任意一个不同就是非同源.
    • 概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
      比如:有一个Ajax的var xhr=new XMLHttpRequest()的xhr对象,在a网址里发生请求到一个非同源的b网址,会请求的报错.
    不跨域会出现图片的的报错
    • 跨域就是为了解决这个问题,实现非同源网页之间的数据传输和通信.

    跨域常见方式

    • JSONP(JSON with Padding 填充式JSON 或参数式JSON)

    • CORS(Cross-Origin Resource Sharing,跨源资源共享)

    • HTML5的window.postMessage
      window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
      window.postMessage允许两个窗口/帧之间跨域发送数据消息。从本质上讲,window.postMessage是一个跨域的无服务器垫片的Ajax。

    • 降域: document.domain
      使用条件
      有其他页面 window 对象的引用,
      二级域名相同,
      协议相同,
      端口相同

    //在页面 http://www.example.com/a.html 中设置document.domain:
    <iframe src="example.com/b.html" id="iframe" onload="test()"></iframe>
        <script>
            document.domain='example.com';//设置成主域
            function test(){
                alert(document.getElementById('iframe').contentWindow);
            }
        </script>
    //在页面 http://example.com/b.html中也设置document.domain
    <script>
            document.domain='example.com';//在iframe载入的这个页面也设置 document.domain与主页面相同
    </script>
    //而且是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值
    

    3: JSONP 的原理是什么

    • JSONP (JSON with Padding)是一个简单高效的跨域方式,html中的script标签可以加载并执行其他域的JavaScript,于是我们可以通过script标记来动态加载其他域的资源,例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。

    • .css,.js,图片的引用和jsonp跨域拿到js方法有什么区别 ?
      相同点:
      在html里.js,图片的引用和jsonp拿到js的方法是一样的,都是从服务器那到js文件然后插入到html里.
      不同:
      只不过出发点是不一样,前者是加载资源,后者为了跨域拿后台返回的js.
      jsonp还多了一个回调,区别是出发点和应用方法不同,但都有从服务器返回的资源.

    4: CORS是什么

    • CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通.CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败.跨域后浏览器不会返回数据.

    • **简单请求 **

    浏览器将CORS请求分成两类:简单请求(simple request)和
    简单请求条件
    1) 请求方法是以下三种方法中的一个:
    HEAD
    GET
    POST
    2)HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
    凡是不同时满足上面两个条件,就属于非简单请求
    
    
    • 使用
      在服务器后台设置header属性Access-Control-Allow-Origin
      它的值是请求时Origin字段的值或者 * ,* 表示接受任意域名的请求。
    app.get('/getNews', function(req, res){
    
        var news = [
            "第11日前瞻:中国冲击4金 博尔特再战200米羽球",
        ]
        var data = [];
        for(var i=0; i<3; i++){
            var index = parseInt(Math.random()*news.length);
            data.push(news[index]);
            news.splice(index, 1);
        }
        res.header("Access-Control-Allow-Origin", "http://a.jrg.com:8080"); //代表只接受http://a.jrg.com:8080网址的请求
        //res.header("Access-Control-Allow-Origin", "*"); //*表示接受任意域名的请求
    
        res.send(data);
    })
    
    • 非简单请求
      是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
      非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight).
      浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
    • CORS与JSONP的比较
      CORS与JSONP的使用目的相同,但是比JSONP更强大,但CORS不支持IE6.7.8.
      JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据.

    跨域问题
    跨域10种方式

    5: 演示三种常用以上跨域的解决方式

    • jsonp
      参考阮一峰

    • 先在客户端上设置 新 Hosts
      127.0.0.1 a.jrg.com
      127.0.0.1 b.jrg.com
      127.0.0.1 jrg.com

    router.js

    
    app.get('/getNews', function(req, res){
    var news = [
    '我没有特别的才能,只有强烈的好奇心。永远保持好奇心的人是永远进步的人。——爱因斯坦',
    
    '爱因斯坦认为他之所以取得成功,原因在于他具有狂热的好奇心.',
    '求知欲,好奇心这是人的永恒的,不可改变的特性。哪里没有求知欲,哪里便没有学校。——苏霍姆林斯基',
    
    '孩子提出的问题越多,那么他在童年早期认识周围的东西也就愈多,在学校中越聪明,眼睛愈明,记忆力愈敏锐。要培养自己孩子的智力,那你就得教给他思考。——苏霍姆林斯基',
    
    '我想起了自己小学的学习经历,终于理解了为什么小时候成绩好,我那时候确实好奇心非常强烈.',
    '人的内心里有一种根深蒂固的需要——总想感到自己是发现者、研究者、探寻者。在儿童的精神世界中,这种需求特别强烈。但如果不向这种需求提供养料,即不积极接触事实和现象,缺乏认识的乐趣,这种需求就会逐渐消失,求知兴趣也与之一道熄灭。(苏霍姆林斯基)',
    
    '生活的全部意义在于无穷地探索尚未知道的东西,在于不断地增加更多的知识。——左拉'
        ]
        var data = [];
        for(var i=0; i<3; i++){
            var index = parseInt(Math.random()*news.length);
            data.push(news[index]);
            news.splice(index, 1);//把data数组里已经有的元素从news数组里删除,保证不重复上一步拿到的新闻
        }
        var cb = req.query.callback
        if(cb){
            res.send(cb + '('+ JSON.stringify(data) + ')');
        }else{
            res.send(data);
        }   
    })
    
    

    index.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>news</title>
    <style>
      .container{
        width: 900px;
        margin: 0 auto;
      }
    </style>
    </head>
    <body>
      <div class="container">
        <ul class="news">
          <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
          <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li> 
          <li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
        </ul>
        <button class="change">点我换一组</button>
      </div>
      
    <script>
      
      $('.change').addEventListener('click', function(){
        var script = document.createElement('script');
        script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml';//必须是'http://xxx.com:8080/的形式
       
        document.head.appendChild(script);
        document.head.removeChild(script);
      })
      
      function appendHtml(news){
        var html = '';
        for( var i=0; i<news.length; i++){
          html += '<li>' + news[i] + '</li>';
        }
        console.log(html);
        $('.news').innerHTML = html;
      }
      function $(id){//$函数,发请求前和点击换一组都调用.传入参数,直接返回.
        return document.querySelector(id);//替换$(id)为document.querySelector(id),因为浏览器不支持jquery库
      }
    </script>
    
    </body>
    
    </html>
    
    

    结合上面的案例说下jsonp的本质:

    
    后台判断:
     var cb = req.query.callback
        if(cb){
            res.send(cb + '('+ JSON.stringify(data) + ')');
        }
    
    场景:
    A网访问跨域的B网资源:
    在A网通过给script标签的src赋值一个网址.
    例子: var script = document.createElement('script');
        script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml'
    
    这个网址带有回调的方法名.加载script脚本到B网页,B网页发回消息去调用在A网页脚本里定义的回调函数.callback.
    

    6.CORS案例

    QQ20170512-220224-HD.gif
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>news</title>
    <style>
     .container{
       width: 900px;
       margin: 0 auto;
     }
    </style>
    </head>
    <body>
     <div class="container">
       <ul class="news">
         <li>CORS练习</li>
         <li>男双力争会师决赛 </li> 
         <li>女排将死磕巴西!</li>
       </ul>
       <button class="change">换一组</button>
     </div>
     
    <script>
     
     $('.change').addEventListener('click', function(){
       var xhr = new XMLHttpRequest();
       xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
       xhr.send();
       xhr.onreadystatechange = function(){
         if(xhr.readyState === 4 && xhr.status === 200){
           appendHtml( JSON.parse(xhr.responseText) )
         }
       }
       window.xhr = xhr//why?
     })
     function appendHtml(news){
       var html = '';
       for( var i=0; i<news.length; i++){
         html += '<li>' + news[i] + '</li>';
       }
       console.log(html);
       $('.news').innerHTML = html;
     }
     function $(id){
       return document.querySelector(id);
     }
    </script>
    </html>
    

    router.js

    
    app.get('/getNews', function(req, res){
    
        var news = [
            "第11日前瞻:中国冲击4金 博尔特再战200米羽球",
            "正直播柴飚/洪炜出战 男双力争会师决赛",
            "女排将死磕巴西!郎平安排男陪练模仿对方核心",
            "没有中国选手和巨星的110米栏 我们还看吗?",
            "中英上演奥运金牌大战",
            "博彩赔率挺中国夺回第二纽约时报:中国因对手服禁药而丢失的奖牌最多",
            "最“出柜”奥运?同性之爱闪耀里约",
            "下跪拜谢与洪荒之力一样 都是真情流露"
        ]
        var data = [];
        for(var i=0; i<3; i++){
            var index = parseInt(Math.random()*news.length);
            data.push(news[index]);
            news.splice(index, 1);
        }
        res.header("Access-Control-Allow-Origin", "http://jrg.com:8080"); 
        //res.header("Access-Control-Allow-Origin", "*"); 
        res.send(data);
    

    7.document.domain降域

    QQ20170513-104421-HD.gif

    情景:a.html里面嵌入iframe元素,且这个iframe是<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>,而b.html就是个其中src规定显示在 iframe 中的文档的地址,也是绝对 URL - 指向其他站点(比如 src="www.example.com/index.html"),这里是个非同源的b.html

    //a.html文件
    
    <html>
    <style>
      .ct{
        width: 910px;
        margin: auto;
      }
      .main{
        float: left;
        width: 450px;
        height: 300px;
        border: 1px solid #ccc;
      }
      .main input{
        margin: 20px;
        width: 200px;
      }
      .iframe{
        float: right;
      }
      iframe{
        width: 450px;
        height: 300px;
        border: 1px dashed #ccc;
      }
    </style>
    
    <div class="ct">
      <h1>使用降域实现跨域</h1>
      <div class="main">
        <input type="text" placeholder="http://a.jrg.com:8080/a.html">
      </div>
    
      <iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>
    
    </div>
    
    
    <script>
    //URL: http://a.jrg.com:8080/a.html
    document.querySelector('.main input').addEventListener('input', function(){
      console.log(this.value);
      window.frames[0].document.querySelector('input').value = this.value;
    })
    
    document.domain = "jrg.com"//降域关键代码
    </script>
    </html>
    
    
    
    //b.html
    <html>
    <style>
        html,body{
            margin: 0;
        }
        input{
            margin: 20px;
            width: 200px;
        }
    </style>
    
        <input id="input" type="text"  placeholder="http://b.jrg.com:8080/b.html">
    <script>
    
     
    document.querySelector('#input').addEventListener('input', function(){
        window.parent.document.querySelector('input').value = this.value;
    })
    document.domain = 'jrg.com';
    </script>
    </html>
    
    

    • window.frames[0].postMessage
    //a.html文件
    
    <html>
    <style>
        .ct{
            width: 910px;
            margin: auto;
        }
        .main{
            float: left;
            width: 450px;
            height: 300px;
            border: 1px solid #ccc;
        }
        .main input{
            margin: 20px;
            width: 200px;
        }
        .iframe{
            float: right;
        }
        iframe{
            width: 450px;
            height: 300px;
            border: 1px dashed #ccc;
        }
    </style>
    
    <div class="ct">
        <h1>使用postMessage实现跨域</h1>
        <div class="main">
            <input type="text" placeholder="http://a.jrg.com:8080/a.html">
        </div>
    
        <iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>
    
    </div>
    
    
    <script>
    //URL: http://a.jrg.com:8080/a.html
    $('.main input').addEventListener('input', function(){
        console.log(this.value);
        window.frames[0].postMessage(this.value,'*');//window.frames[0]是window子窗口的第一个.*代表任意地址
    
    })
    window.addEventListener('message',function(e) {
            $('.main input').value = e.data
        console.log(e.data);
    });
    
    //
    window.addEventListener('message',function(e){
      $('.mian input').value = e.data;
    })
    
    
    function $(id){
        return document.querySelector(id);
    }
    </script>
    </html>
    
    //b.html文件:即被内嵌的iframe链接文档url.
    <html>
    <style>
        html,body{
            margin: 0;
        }
        input{
            margin: 20px;
            width: 200px;
        }
    </style>
    
        <input id="input" type="text"  placeholder="http://b.jrg.com:8080/b.html">
    <script>
    
     
     //传出数据到内嵌此窗口的父窗口即a.html.
    $('#input').addEventListener('input', function(){
        window.parent.postMessage(this.value, '*');//
        //返回当前窗口的父窗口对象.如果一个窗口没有父窗口,则它的 parent 属性为自身的引用.
        //如果当前窗口是一个 <iframe>, <object>, 或者 <frame>,则它的父窗口是嵌入它的那个窗口
    })
    
    //接收信息
    window.addEventListener('message',function(e) {
            $('#input').value = e.data
        console.log(e.data);
    });
    function $(id){
        return document.querySelector(id);
    }   
    </script>
    </html>
    

    跨域实例的代码参考

    相关文章

      网友评论

          本文标题:js__JSONP__跨域

          本文链接:https://www.haomeiwen.com/subject/prkatxtx.html