浏览器安全的基石是“同源政策”
1.同源政策含义
A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。
不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。
所谓“同源”指的是”三个相同
协议相同
域名相同
端口相同
举例来说,http://www.example.com:80/dir/page.html这个网址,
协议是http://,
域名是www.example.com,
端口是80(默认端口可以省略)。
2.跨域
跨域解决方案
1、 通过jsonp跨域 !
2、 document.domain + iframe跨域 !
3、 location.hash + iframe !
4、 window.name + iframe跨域 ?
5、 postMessage跨域 ?
6、 跨域资源共享(CORS) ?
7、 nginx代理跨域 ?
8、 nodejs中间件代理跨域 ?
9、 WebSocket协议跨域 ?
2.1 JSONP跨域
一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
它的基本思想是,网页通过添加一个script元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
步骤:
脚本动态插入
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
上面代码通过动态添加script元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于 JSONP 是必需的。
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
foo({
"ip": "8.8.8.8"
});
由于script元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用JSON.parse的步骤。
凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>
推荐一篇文章
https://blog.csdn.net/hansexploration/article/details/80314948
2.2document.domain跨域
此方法有个前提:只有主域名相同的情况下方可使用此方法
介绍document.domain跨域之前我们先补充几个知识点:
URL的组成
http://mail.163.com/index.html
http://:这个是协议,也就是HTTP超文本传输协议,也就是网页在网上传输的协议。
mail:这个是服务器名,代表着是一个邮箱服务器,所以是mail.
163.com:这个是域名,是用来定位网站的独一无二的名字。
mail.163.com:这个是网站名,由服务器名+域名组成。
/:这个是根目录,也就是说,通过网站名找到服务器,然后在服务器存放网页的根目录
index.html:这个是根目录下的默认网页(当然,163的默认网页是不是这个我不知道,只是大部分的默认网页,都是index.html)
http://mail.163.com/index.html:这个叫做URL,统一资源定位符,全球性地址,用于定位网上的资源。
163.com,他的域名是163.com,他想建立一个www服务器,所以有了www.163.com.
他又想玩邮箱服务器,所以,mail.163.com也有了。
一级域名(根域名),如:sojson.com、baidu.com、sina.com、sina.com.cn、sina.cn.net 等等。
二级域名 是指增加了一级,包括www。如:www.sojson.com、icp.sojson.com、zhidao.baidu.com、www.baidu.com 等等。有人把www.sojson.com叫一级域名这是错误的。
什么是主域名相同
huhu.com
a.huhu.com
b.a.huhu.com
这是三个不同的域名,但是主域名是相同的 都是 huhu.com, 不是这种形式的那么都不能用document.domain方法跨域。
console.lg(document.domain)
用来得到当前网页的域名。
利用document.domain 实现跨域:
前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域.
Javascript出于对安全性的考虑,而禁止两个或者多个不同域的页面进行互相操作。
相同域的页面在相互操作的时候不会有任何问题。
实现原理:通过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 user = 'admin';
</script>
2.)子窗口:(http://child.domain.com/b.html)
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>
window.parent 属性返回当前窗口的父窗口。
2.3 location.hash + iframe跨域
location对象的hash属性
location对象是javascript管理地址栏的内置对象。
location.href 就管理页面的url,用 location.href=url 就可以直接将页面重定向 url。location.hash 则可以用来获取或设置页面的标签值。比如 http://domain/#admin 的location.hash="#admin"
#代表网页中的一个位置。其右面的字符,就是该位置的标识符。比如,
http://www.example.com/index.html#print
就代表网页index.html的print位置。浏览器读取这个URL后,会自动将print位置滚动至可视区域。
为网页位置指定标识符,有两个方法。
一是使用锚点,比如<a name="print"></a>
二是使用id属性,比如<div id="print" >
在第一个#后面出现的任何字符,都会被浏览器解读为位置标识符。这意味着,这些字符都不会被发送到服务器端。
比如,下面URL的原意是指定一个颜色值:
http://www.example.com/?color=#fff
但是,浏览器实际发出的请求是:
GET /?color= HTTP/1.1
Host: www.example.com
实现原理: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
(我看明白了 日后再回来总结…)
https://www.cnblogs.com/zichi/p/4621963.html
http://rolfzhang.com/articles/380.html
http://www.cnblogs.com/skylar/p/4101443.html#comments
2.2WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
2.3cors
浏览器发出的请求的头信息有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。
2.4postMessage
HTML5提供的新API – postMessage
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
用法:postMessage(data,origin)方法接受两个参数
1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。