注:本文主要用于自主总结,后期会更新。如果要最全面的,可以参考大神的文章正确面对跨域、别慌
好啦,进入正题。
今天我们来了解一下“跨域”。
究竟什么是跨域呢?首先我们得先了解一下什么是域。
一、域是什么?
在今天很多人都有意识或无意识的跟域这个东西打过交道。如果你在公司里使用电脑,并且你的电脑接入了公司的局域网,那你的电脑很可能就在一个域中。
如何查看你的电脑是否连接到一个域中,以Windows为例,右击我的电脑 –>属性,可以看到,我现在使用的这台电脑就加入了一个域。 域已经成为绝大多数公司组织、连接电脑的一种方式。
那么我们究竟为什么要使用域?它能给我们带来什么好处呢?
假设你是公司的系统管理员,你们公司有一千台电脑。如果你要为每台电脑设置登录帐户,设置权限(比如是否允许登录帐户安装软件),那你要分别坐在这一千台电脑前工作。如果你要做一些改变,你也要分别在这一千台电脑上修改。相信没有哪个管理员想要用这种不吃不喝不睡觉的方式来工作,所以就应运而生了域的概念。
还以Windows为例,在微软的世界中,一个域是由一个或多个域控制器(domain controller)来控制的(其实域控制器并不神秘,无非就是装了一些特别软件的电脑)。其他的电脑加入该域,就要接受域控制器的控制。域控制器中有两个重要的表,一个是加入该域的电脑的列表,另一个表用来保存叫做活动目录(Active Directory)的东西。活动目录就是你登录公司网络的帐户。活动目录中存储着你的权限。你在某台电脑上登录,你键入用户名和密码,你的电脑首先要把你的登录信息发送到域控制器,域控制器首先核实你的登录信息是否正确,然后把一个叫access key的东西返还给你登录的电脑。这个access key中就包含着你的权限,由它来决定你是否可以安装软件,或者使用打印机等等。
有了域以后,作为管理员,你就可以坐在一台电脑前,登录到域控制器上,对一切权限进行控制,而不用跑到每台电脑前进行设置了。工作效率提高了很多,但是还不够。假设公司有一千名员工,你是否要在域控制器中对这一千个人分别进行权限的设置呢?这听起来也是一件很麻烦的事。其实很多员工的权限都是相同的,那我们可不可以对这些相同的权限只设置一次,然后将该权限分配给相关的员工呢?
答案就是使用分组(Group)。比如在学校里,我们设置学生组和教师组。在某台电脑上我们设置一个共享文件夹,学生组的权限是不可访问,而教师组可以对该文件夹进行访问。我们将不同的用户放入不同的分组里,然后对组进行权限设置,这样就免去了我们要对每个用户进行设置的麻烦。比组更大的单位是组织单位(Organization Unit),一个组织单位可以包含用户,组,资源(电脑,打印机等),还可以包含其他组织单位。
举个简单的例子,有一个商场中的电脑加入了该商场的域,但是该电脑放置在公共场合,有很大的风险,所以我们可以将该电脑放置在一个单独的组织单位中,然后对该组织单位进行权限设置,比如不论谁在该组织单位中登录,都不可以修改他的密码。在很多的实际情况中,一个公司又有下面的子公司,所以就造成母公司有一个域,而子公司也有一个单独的域。母公司的域与子公司的域如何联系起来呢?我们可以在它们之间建立一种叫信任(Trust)的关系。如果母公司的帐户想要能够登录到子公司的域中,子公司的域就要对母公司的域建立信任关系。当母公司域的帐户想要登录到子公司域中时,子公司域由于信任母公司的域,所以它会听从从母公司域的域控制器返回的access key。反过来,由于母公司的域没有建立对子公司的信任,所以如果子公司的帐户想要登录到母公司的域中是不可能的。
注:虽然我们上面的例子都是Windows的域,但域这个概念绝不是微软独创的。你也可以在Linux中使用一个叫Samba的软件来创建域。如果你要用Windows搭建一个域环境的话,要注意两点:
(1) 加入域不能使用Home版的Windows操作系统(顾名思义它是给你在家用的,而你家里是不用搭建域的)。
(2) 域控制器不能使用web edition server,因为它没有安装活动目录。 在现实环境中,有很多公司在域中使用多个域控制器,因为如果只使用一个域控制器的话,一旦域控制器不能正常工作,整个域就会瘫痪。在Samba中,Linux使用两个域控制器,一个是主域控制器,一个是备份域控制器。一旦主域控制器发生故障,备份域控制器就开始工作,直到主域控制器恢复正常。备份域控制器就是一个主域控制器的拷贝,但是它的数据都是只读的,也就是说管理员不能在备份域控制器上修改权限。
二、什么是域名
域,就像是一座城,那有了城,这座城当然就得有名字啦,我们总不能一直叫“那座城”吧。
而域名呢,就是你在这个体系中的名称或地址。
可能有人对于域名有过了解,我们先回顾一下域名地址的组成:
http:// (协议号)
www (子域名)
google (主域名)
8080 (端口号)
script/jquery.js (请求的地址)
* 当协议、子域名、主域名、端口号都相同时,属于同一个“域”。
三、什么是跨域
从上文我们可以知道,一个域名包含协议号、子域名、主域名、端口号以及请求的地址。当协议、子域名、主域名、端口号都相同时,属于同一个“域”。
既然有同“域”,那当然也有跨域了。那什么叫跨域呢?
“当协议、子域名、主域名、端口号中任意一各不相同时,都算不同的“域”。 不同的域之间相互请求资源,就叫“跨域”。”
也许光说的话大家不好理解,那么正好拾人牙慧,给大家举些例子。
http://www.111.com/index.html 调用 http://www.111.com/server.php (非跨域)
http://www.111.com/index.html 调用 http://www.222.com/server.php (主域名不同:123/456,跨域)
http://abc.111.com/index.html 调用 http://def.111.com/server.php (子域名不同:abc/def,跨域)
http://www.111.com:8080/index.html 调用 http://www.111.com:8081/server.php (端口不同:8080/8081,跨域)
http://www.111.com/index.html 调用 https://www.111.com/server.php (协议不同:http/https,跨域)
需要注意一点:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
看完上面的例子,大家想必就明白什么叫跨域了。
四、如何跨域?
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
同源策略限制了一下行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
那我要跨域访问的话,又该如何呢?
解决办法:
1、JSONP:
jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议。
一个是描述信息的格式,一个是信息传递双方约定的方法。
jsonp的产生:
1.AJAX直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好.
2.不过我们在调用js文件的时候又不受跨域影响,比如引入jquery框架的,或者是调用相片的时候
3.凡是拥有scr这个属性的标签都可以跨域例如<script><img><iframe>
4.如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.
5.而json又是一个轻量级的数据格式,还被js原生支持
6.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端,
要注意JSONP只支持GET请求,不支持POST请求。
demo1:基于script标签实现跨域
举个例子:我在http://study.cn/json/jsonp/jsonp_2.html下请求一个远程的js文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
var message = function(data) {
alert(data[1].title);
};
</script>
<script type="text/javascript" src="http://web.cn/js/message.js"></script>
</head>
<body>
<div id='testdiv'></div>
</body>
</html>
远程的message.js文件是
message([
{"id":"1", "title":"天津新闻联播,雷人搞笑的男主持人"},
{"id":"2", "title":"楼市告别富得流油 专家:房价下跌是大概率事件"},
{"id":"3", "title":"法国人关注时事 八成年轻人每天阅读新闻"},
{"id":"4", "title":"新闻中的历史,历史中的新闻"},
{"id":"5", "title":"东阳新闻20140222"},
{"id":"6", "title":"23个职能部门要增加新闻发布频次"},
{"id":"7", "title":"《贵州新闻联播》 中国美丽乡村"},
{"id":"8", "title":"朝韩离散家属团聚首轮活动结束"},
{"id":"9", "title":"索契冬奥会一天曝出两例兴奋剂事件"},
{"id":"10", "title":"今天中国多地仍将出现中度霾"}
]);
这个时候我们得到的相应头是:
这样就实现跨域成功了,因为服务端返回数据时会将这个callback参数(message)作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
demo2: 基于script标签实现跨域
让远程js知道它应该调用的本地函数叫什么名字,只要服务端提供的js脚本是动态生成的就好了,这样前台只需要传一个callback参数过去告诉服务端,我需要XXX代码,于是服务端就会得到相应了.
例如 在http://study.cn/json/jsonp/jsonp_3.html页面请求 http://192.168.31.137/train/test/jsonpthree
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
var messagetow = function(data){
alert(data);
};
var url = "http://192.168.31.137/train/test/jsonpthree?callback=messagetow";
var script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
</body>
</html>
得到的响应头是:
demo3: 基于jquery跨域
那么如何用jquery来实现我们的跨域呢???jquery已经把跨域封装到ajax上了,而且封装得非常的好,使用起来也特别方便
如果是一般的ajax请求:
$.ajax({
url:'http://192.168.31.137/train/test/testjsonp',
type : 'get',
dataType : 'text',
success:function(data){
alert(data);
},
error:function(data){
alert(2);
}
});
那么在浏览器中会报错:
jsonp形式的ajax请求:并且通过get请求的方式传入参数,注意:跨域请求是只能是get请求不能使用post请求
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var name = 'chenshishuo';
var sex = 'man';
var address = 'shenzhen';
var looks = 'handsome ';
$.ajax({
type : 'get',
url:'http://192.168.31.137/train/test/testjsonp',
data : {
name : name,
sex : sex,
address : address,
looks : looks,
},
cache :false,
jsonp: "callback",
jsonpCallback:"success",
dataType : 'jsonp',
success:function(data){
alert(data);
},
error:function(data){
alert('error');
}
});
});
</script>
</head>
<body>
<input id='inputtest' value='546' name='inputtest'>
<div id='testdiv'></div>
</body>
</html>
jsonp 传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
jsonpCallback 自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
看看请求头和相应头吧
请求头:jquery会自动带入callback参数,当服务端获取到这个参数后,返回回来.(响应头)
2、代理
例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
用的大多数的方式就是用ifram代理。
如果你想在http://a.study.cn/a.html页面中通过ajax直接请求页面http://b.study.cn/b.html,即使你设置了相同的document.domain也还是不行的.
所以修改document.domain的方法只适用于不同子域的框架(父类与子类)间的交互。
如果想通过使用ajax的方法去与不同子域间的数据交互或者是js调用,只有两种方法,一种是使用jsonp的方法外,还有一种是使用iframe来做一个代理。
原理就是让这个 iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据 的,
然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发 送ajax请求,然后收到的数据我们也可以获得了。
下面是一个例子:
demo4: 通过iframe来跨子域
http://a.study.cn/a.html 请求 http://b.study.cn/b.html
在a.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
function test() {
alert(document.getElementById('a').contentWindow);
}
</script>
</head>
<body>
<iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>
在b.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
</script>
</head>
<body>
我是b.study.cn的body
</body>
</html>
运行效果截图:
我们就可以通过js访问到iframe中的各种属性和对象了
这里需要注意的是:
基于iframe实现的跨域要求两个域具有aa.xx.com,bb.xx.com 这种特点,
也就是两个页面必须属于一个基础域(例如都是xxx.com),使用同一协议和同一端口,这样在两个页面中同时添加document.domain,就可以实现父页面调用子页面的函数
要点就是 :通过修改document.domain来跨子域
3、PHP端修改header(XHR2方式)
在php接口脚本中加入以下两句即可:
header('Access-Control-Allow-Origin:*');//允许所有来源访问
header('Access-Control-Allow-Method:POST,GET');//允许访问的方式
好啦,以上就是关于域的一些总结。大家如果对于上文总结的内容有自己的看法,欢迎留言评论,我们一起交流交流呀~~
本文参考文章:
https://www.cnblogs.com/chenshishuo/p/4919224.html?tdsourcetag=s_pcqq_aiomsg
https://blog.csdn.net/lambert310/article/details/51683775?tdsourcetag=s_pcqq_aiomsg