javascript基础--跨域

引言

为什么会有跨域,什么是跨域,这些疑问在没有接触它之前都是正常的。简单的来讲,是因为某些人利用加载外部域的外部脚本代码来搜集原网站信息,以致各种信息泄露,财产损失等现象发生。所以javascript出于安全的角度,提出同源策略,禁止加载不同域下的资源。但是我们开发人员有时的确也有需要使用外部资源,怎么办呢?这就是跨域。

1.什么是跨域

域名的组成:
这里写图片描述
判定方法:

  • 1.当协议,子域名,主域名,端口号四个之间任意一个不同时,都算不同域
  • 2.不同域名之间请求资源就算跨域

示例:

//请求不同的资源
http://www.baidu.com/index.html  http://www.baidu.com/1.png  非跨域
//协议不同
http://www.baidu.com/index.html  https://www.baidu.com/index.html 跨域 
//子域名不同
http://www.baidu.com/index.html  http://bbs.baidu.com/index.html 跨域 
//主域名不同
http://www.baidu.com/index.html  http://www.sina.com/index.html 跨域 
//端口号不通
http://www.baidu.com:80/index.html  http://www.baidu.com:81/index.htm 跨域

2.跨域方法一: document.domain

首选得知道document.domain是什么?
释义:document.domain返回当前文档的域名,可以试试,我用的bing搜索,没有广告哦。

这里写图片描述

可以打印那么可以赋值修改domain吗?试试就知道了

这里写图片描述

可见document.domain修改是有原则的,具体有两条

  • 可以修改为本身,相当于没做修改(感觉被耍了)
  • 可以修改为本身的主域名(跨域就从这里做文章)

这么讲,假设你们班上没有同名的人。你叫李小明,别人可以叫你李小明,也可以叫你小明,不可能叫你李大明,周小明。老师提问,叫到李小明,你肯定一下子就站起来了。如果恰好你们班上有个人叫王小明,那老师叫小明,谁又该站起来呢。所以跨域也是这个道理,利用这种歧义,让“名字”相同,“姓氏”不同的人,实现共享信息

举个例子:www.baidu.com下面www是子域名,baidu.com是主域名。所以要修改,只有两种可能,其它情况都不行

  • 第一,修改为www.baidu.com
  • 第二,修改为baidu.com

我们实现跨域主要是依靠第二个结果来做文章。所以document.domain只对主域名相同,子域名不同的情况实现跨域。为什么这样讲?看看例子。
准备好两个页面:www.a.com cn.a.com —> 两个域名的主域名都是a.com,子域名不同
实现方式:借助Frame对象

www.a.com a.html页面

document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://cn.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
    //获取Frame对象文档内容
    var doc = ifr.contentDocument || ifr.contentWindow.document;
    // 在这里操纵b.html
    alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeVaue);
};

cn.a.com b.html页面

document.domain = 'a.com';

2.跨域方法二:jsonp

这里就不讨论jsonp和json的区别了,就像java和javascript名字相似,其实质是两种东西。
jsonp其实就是利用script标签不受同源策略限制为出发点的。像下面这样

//jquery CDN服务,引用不同域文件完全不受同源策略限制
<script src="//cdn.bootcss.com/jquery/3.1.1/core.js"></script>

我们先构建jsonp的雏形
A域下有个a.html,B域下有个b.js
A域下a.html页面内容为:

<script type="text/script">
    function info(my){
        console.log(my.info);
    }
</script>
<script type="text/script" src="B/b.js"></script>

B域下b.js的内容为:

info({info:"kingboss"});

结果为:kingboss
所以jsonp的实质是,另一个域下的文件传递一个callback执行函数,函数名与原域所定义的函数名相同。

当然世界是多变的,代码是活的,所以我们可以动态的创建script标签,引入可变的src地址
A域下a.html页面内容改为:

<script type="text/script">
    function info(my){
        console.log(my.info);
    }
    function addScript(src){
        var script = document.createElement('script');
        script.setAttribute("type","text/javascript");
        script.src = src;
        document.body.appendChild(script);
    }
    window.onload = function(){
        addScript("B/b.js");
    }

</script>

B域下b.js的内容不变:

info({info:"kingboss"});

有人就会问了,加入B域下的文件不知道A域下的函数名怎么办?简单,那就让A域下的文件告诉B就ok了,具体的告知方式就是在url部添加callback参数
A域下a.html页面内容改为:

<script type="text/script">
    function info(my){
        console.log(my.info);
    }
    function addScript(src){
        var script = document.createElement('script');
        script.setAttribute("type","text/javascript");
        script.src = src;
        document.body.appendChild(script);
    }
    window.onload = function(){
        addScript("B/b.js?callback=info");
    }

</script>

B域下的文件根据callback参数动态生成执行函数
jquery的jsonp实现上面的例子

<script src="//cdn.bootcss.com/jquery/3.1.1/core.js"></script>
<script type="text/javascript">
    $(function(){
        $.ajax({
             type: "get",
             async: false,
             url: "B/b.js",
             dataType: "jsonp",
             jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
             jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
             success: function(my){
                 console.log(my.info)
             },
             error: function(){
                 alert('fail');
             }
         });

    })
</script>

注意:jsonp只支持get请求,原因不是很清楚

3.跨域方法三:location.hash

前面我们讲过document.domain只适用于主域名相同,子域名不同的情况,那么主域名也不同呢?又该怎么办。所以就有了进阶版,location.hash传值方法。
什么是location.hash,其实它是js用来管理内部地址的对象,内容为:

属性描述
hash从井号 (#) 开始的 URL(锚)
host主机名和当前 URL 的端口号
hostname当前 URL 的主机名
href完整的 URL
pathname当前 URL 的路径部分
port当前 URL 的端口号
protocol当前 URL 的协议
search从问号 (?) 开始的 URL(查询部分)

方法:利用location.hash进行传值,选择其传值的理由是改变hash指不会刷新页面。A域下a.html通过创建iframe指向B域下b.html,由于不同域,所以B域下会创建A域的a1.html(代理页面),代理页面a1.html和a.html同属一个域,因此可以修改。缺点:数据完全暴露在url中,容量也有限制
逻辑图
这里写图片描述

A域下 a.html

<script type="text/javascript">
    function startRequest(){
        var ifr = document.createElement('iframe');
        ifr.style.display = 'none';
        ifr.src = 'B/b.html#data';
        document.body.appendChild(ifr);
    }

    function checkHash() {
        try {
            //获取hash传入的data值
            var data = location.hash ? location.hash.substring(1) : '';
            if (console.log) {
                console.log('Now the data is '+data);
            }
        } catch(e) {};
    }
    setInterval(checkHash, 2000);
</script>

B域下 b.html

<script type="text/script">
    switch(location.hash){
        case '#paramdo':
            callBack();
            break;
        case '#paramset':
            //do something……
            break;
    }

    function callBack(){
        try {
            parent.location.hash = 'somedata';
        } catch (e) {
            // ie、chrome的安全机制无法修改parent.location.hash,
            // 所以要利用一个中间的B域下的代理iframe
            var ifrproxy = document.createElement('iframe');
            ifrproxy.style.display = 'none';
            ifrproxy.src = 'A/a1.html#somedata';    // 注意该文件在A域下
            document.body.appendChild(ifrproxy);
        }
    }
</script>

A域下 a1.html

<script type="text/javascript">
    //因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
    parent.parent.location.hash = self.location.hash.substring(1);
</script>

4.跨域方法四:window.name

先了解一下什么是window.name,以及它有什么特性。window.name方法传值其实和location.hash类似,也依靠代理文件实现。共需要三份文件,A域下的a.html;B域下的b.html;A域下的a1.html(空代理文件)

B域下的b.html

<script type="text/javascript">
    window.name = 'data';    // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右
</script>

A域下的a.html

<script type="text/javascript">
    var state = 0, 
    //动态地创建iframe
    iframe = document.createElement('iframe'),
    iframe.src = 'B/b.html';
    document.body.appendChild(iframe);

    loadfn = function() {
        if (state === 1) {
            var data = iframe.contentWindow.name;    // 读取数据
            alert(data);    //弹出'data'
        } else if (state === 0) {
            //改变iframe的location指向代理文件
            iframe.contentWindow.location = "A/a1.html";    // 设置的代理文件
        }  
    };
    //兼容,能力检测
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload  = loadfn;
    }
    //销毁iframe
    iframe.contentWindow.document.write('');//文档内容清空
    iframe.contentWindow.close();//关闭子窗口
    document.body.removeChild(iframe);//去除tag

</script>

疑问:
1.什么是window.name?
解释:name 属性可设置或得到窗口的名称,其值为字符串;该名称是在 open() 方法创建窗口时指定的或者使用一个 frame 标记的 name 属性指定的,默认情况下 name 属性值是为空的。窗口的 name 属性可以用于a或form标签的 target 属性值,这样表示超链接文档或表单提交结果应该显示于指定 name 的窗口或框架中。
特点是:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。—->这也解释了为什么A域的代理文件可以访问到B域的window.name

2.什么是contentWindow属性?
contentWindow : 兼容各个浏览器,可取得子窗口的 window 对象。
contentDocument : Firefox 支持,> ie8 的ie支持。能够以 HTML 对象来返回 iframe 中的文档。可以通过所有标准的 DOM 方法来处理被返回的document对象

解决了上面的疑惑,用图来呈现此逻辑
这里写图片描述

结束语

虽然跨域有很多种方法,但是要折中,比如引入iframe本身就对网页优化有一定的影响,还有location.hash的安全漏洞等问题。所以不要滥用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: pdfjs-dist是一个用于加载和渲染PDF文档的JavaScript库。由于安全原因,浏览器默认情况下会阻止跨域加载PDF文件。要解决这个问题,我们可以通过以下步骤来实现pdfjs-dist跨域加载PDF文档: 1. 在服务器端设置CORS(跨域资源共享)头文件。可以在服务器上的响应头中添加Access-Control-Allow-Origin字段,并将其设置为允许访问的域名,以允许跨域加载PDF文件。 2. 在客户端代码中使用XMLHttpRequest对象来加载PDF文件。可以使用XMLHttpRequest对象发送GET请求,并在请求头中添加origin字段以指定服务器的域名。然后,使用responseType属性设置响应类型为blob,以便以二进制格式接收PDF文件。 3. 在接收到响应后,使用FileReader对象读取二进制数据,并将其转换为PDF文件。可以使用FileReader对象的readAsArrayBuffer方法读取blob响应,并在load事件触发时将结果传递给PDFJS库的load方法。 4. 使用PDFJS库的PDFViewer对象来展示加载的PDF文件。可以创建一个空的div元素作为容器,并使用PDFViewer对象将其与加载的PDF文件关联起来。然后,将PDFViewer对象的container属性设置为之前创建的div元素,并调用PDFViewer对象的render方法来显示PDF文件。 通过以上步骤,我们可以实现pdfjs-dist跨域加载PDF文档的功能。在服务器端进行CORS配置并在客户端代码中使用XMLHttpRequest对象加载PDF文件,最后使用PDFViewer对象展示加载的PDF文件。这样可以解决浏览器默认情况下的跨域加载限制,让我们能够在任意域名下使用pdfjs-dist加载和渲染PDF文档。 ### 回答2: pdfjs-dist是一个开源的JavaScript库,用于在Web浏览器中展示PDF文档。当在网页中使用pdfjs-dist加载远程的PDF文件时,可能会遇到跨域加载的问题。 跨域加载是指Web浏览器限制从不同源加载资源的安全策略。默认情况下,浏览器不允许从一个域加载来自另一个域的内容,这在一定程度上是为了防止恶意代码的执行。 要解决pdfjs-dist跨域加载PDF的问题,我们可以采取以下方法: 1. 设置服务器的响应头: 在加载PDF文件的服务器上,可以设置响应头,允许跨域访问。常见的方法是在响应头中加入Access-Control-Allow-Origin字段,其值为需要允许访问的域名或*,表示允许任意域名进行访问。 2. 使用代理服务器: 可以使用一个中间代理服务器来绕过跨域问题。在代理服务器上发起请求获取PDF文件,并将响应返回给浏览器。这种方法可以在服务器端解决跨域问题,但需要额外的服务器资源支持。 3. 使用服务器端转发: 在服务器端设置API,将PDF文件内容作为响应返回给客户端。客户端通过调用这个API来获取PDF文件内容,避免了直接跨域加载的问题。 总之,解决pdfjs-dist跨域加载PDF的问题有多种方法可选,选择合适的方法取决于具体的应用场景和需求。 ### 回答3: PDF.js是一个用于在Web上渲染PDF文件的开源JavaScript库,它提供了以JavaScript基础的PDF查看器。PDF.js可以跨域加载PDF文件,以下是一些关于如何使用pdfjs-dist来跨域加载PDF的步骤: 1. 首先,下载并在你的网站中引入pdfjs-dist库。你可以从PDF.js的GitHub页面下载最新版本。 2. 在HTML文件中创建一个容器元素来放置PDF查看器: ```html <div id="pdf-viewer"></div> ``` 3. 在JavaScript代码中使用pdfjs-dist库来加载并渲染PDF文件: ```javascript // 设置PDF文件的URL const pdfUrl = 'http://example.com/path/to/pdf-file.pdf'; // 获取容器元素 const container = document.getElementById('pdf-viewer'); // 创建PDF查看器实例 const pdfViewer = new PDFViewer({ container: container }); // 获取PDF文档 PDFJS.getDocument(pdfUrl).promise.then(function(pdfDoc) { // 将PDF文档绑定到查看器实例 pdfViewer.setDocument(pdfDoc); }); ``` 4. 设置正确的跨域请求头(如果需要)。在服务器端,确保使用适当的CORS配置允许来自你网站的跨域请求。 5. 运行你的网站,并在浏览器中查看结果。PDF文件应该成功加载和渲染在指定的容器元素中。 通过以上步骤,你可以使用pdfjs-dist库来跨域加载和显示PDF文件。但是请注意,跨域请求需要在服务器端进行相应的配置,以确保安全性和数据的完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值