1.背景介绍
1.1什么是跨域?
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
广义的跨域:
- 资源跳转: A链接、重定向、表单提交
- 资源嵌入: link script img frame等dom标签,还有样式中background:url()、@font-face() 等文件外链
- 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
1.2那么是什么同源策略呢?
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。同源策略限制以下几种行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- AJAX 请求不能发送
1.3常见的跨域场景
同域名,不同文件路径 【允许通信】
http://www.kimoc.cn/a.js
http://www.kimoc.cn/b.js
http://www.kimoc.cn/text/c.js
同域名,不同端口 【不允许通信】
http://www.kimoc.cn:8080/a.js
http://www.kimoc.cn/b.js
同域名,不同协议 【不允许通信】
http://www.kimoc.cn/a.js
https://www.kimoc.cn/b.js
域名与域名同 IP 【不允许通信】
http://www.kimoc.cn/a.js
http://192.168.1.200/b.js
同域但不同子域名 【不允许通信】
http://www.kimoc.cn/a.js
http://abc.kimoc.cn/b.js
http://demo.kimoc.cn/c.js
不同域名 【不允许通信】
http://www.kimoc.cn/a.js
http://www.kimoc1.cn/b.js
http://www.kimoc2.cn/c.js
2.知识剖析
2.1 常见的跨域方式
JSONP
在使用XMLHTTPRequest对象发送HTTP请求时,会遇到同源策略问题,域不同请求会被浏览器拦截。这时就可以选择绕过,或者说是不使用XMLHTTPRequest对象进行发送跨域HTTP请求。 在平常写html时会发现比如
<script src="http://www.a.com/script/1.js"></script>
<img src="http://www.b.com/1.jpg">
<link href="http://www.c.com/1.css">
这种标签是不会遇到'跨域'问题的,严格上来说,这不是跨域,跨域是指在脚本代码中向非同源域发送HTTP请求,这只是跨站资源请求
那么我们可以试试看用这种跨站资源请求的方式来实现跨域HTTP请求
<!-- HTML表头部分 -->
<!-- javaScript片段1 -->
// 如果jsonp 的请求为GET
if ( ctx.method === 'GET' && ctx.url.split('?')[0] === '/getData') {
// 获取jsonp的callback
let callbackName = ctx.query.callback || 'callback'
let returnData = {
success: true,
data: {
text: 'this is a jsonp api',
time: new Date().getTime(),
}
}
// jsonp的script字符串
let jsonpStr = `;${callbackName}(${JSON.stringify(returnData)})`
// 用text/javascript,让请求支持跨域获取
ctx.type = 'text/javascript'
// 输出jsonp字符串
ctx.body = jsonpStr {
else {
ctx.body = 'hello jsonp'
}
})
NGINX的反向代理
nginx支持配置反向代理,通过反向代理实现网站的负载均衡。这部分先写一个nginx的配置,后续需要深入研究nginx的代理模块和负载均衡模块。 nginx通过proxy_pass_http 配置代理站点,upstream实现负载均衡。
WINDOW.NAME + IFRAME
WINDOW.NAME 传输技术的基本原理:
当在浏览器中打开一个页面,或者在页面中添加一个iframe时即会创建一个对应的window对象,当页面加载另一个新的页面时,window的name属性是不会变的。这样就可以利用在页面动态添加一个iframe然后src加载数据页面,在数据页面将需要的数据赋值给window.name。然而此时承载iframe的parent页面还是不能直接访问,不在同一域下iframe的name属性,这时只需要将iframe再加载一个与承载页面同域的空白页面,即可对window.name进行数据读取。
name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。同源策略依旧防止其他 frame 访问 name 属性。一旦 name 属性获得,销毁 frame 。
在最顶层,name 属性是不安全的,对于所有后续页面,设置在 name 属性中的任何信息都是可获得的。然而 windowName 模块总是在一个 iframe 中加载资源,并且一旦获取到数据,或者当你在最顶层浏览了一个新页面,这个 iframe 将被销毁,所以其他页面永远访问不到 window.name 属性。
<script type="text/javascript">
iframe = document.createElement('iframe');
iframe.style.display = 'none';
var state = 0;
iframe.onload = function() {
if(state === 1) {
var data = JSON.parse(iframe.contentWindow.name);
console.log(data);
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if(state === 0) {
state = 1;
iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';//指向自己根目录下的空文件
}
};
iframe.src = 'http://localhost:8080/data.php';//要请求的不同源数据
document.body.appendChild(iframe);
</script>
也可以node.js或者其他服务端设置cors表头来授权跨域
3.拓展思考
3.1正向代理正向代理类似一个跳板机,代理访问外部资源。
我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。总结来说:正向代理 是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
正向代理的用途:
(1)访问原来无法访问的资源,如google
(2) 可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
6.2反向代理
客户端是无感知代理的存在的,反向代理对外都是透明的,访问者者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。
反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
反向代理的作用:
(1)保证内网的安全,可以使用反向代理提供WAF功能,阻止web攻击大型网站,通常将反向代理作为公网访问地址,Web服务器是内网。
(2)负载均衡,通过反向代理服务器来优化网站的负载
6.3二者的区别
借某乎上一个灵魂画手的图片解释下
4. 参考文献
前端常见跨域解决方案:https://www.cnblogs.com/roam/p/7520433.html
js中几种使用的跨域方法详解:https://www.cnblogs.com/2050/p/3191744.html