跨域,相信你们不管是在工做中仍是在面试中常常遇到这个问题,经常在网上看到别人所整理的一些方法,看似知道是怎么回事,但若是没有动手实践过,总以为本身没有真正的掌握,在这里,经过本身认真思考整理一些经常使用的方法。
不用多讲,做为一名前端开发人员,相信你们都知道跨域是由于浏览器的同源策略所致使的。所谓同源是指"协议+域名+端口"三者相同,即使两个不一样的域名指向同一个ip地址,也非同源。浏览器引入同源策略主要是为了防止XSS,CSRF攻击。javascript
CSRF(Cross-site request forgery),跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
在同源策略影响下,域名A向域名B发送Ajax请求,或操做Cookie、LocalStorage、indexDB等数据,或操做dom,js就会受到限制,但请求css,js等静态资源不受限制css
首先说一下jsonp的原理,例如咱们平时写html的时候经常会使用
<script src="www.b.com/js/jquery.js"></script>这种方式去取放在另外服务器上的静态资源,这个是不受同源策略所限制的,因此咱们利用这一点能够解决跨域的问题。html
主要代码以下:前端
在www.a.com域名写下以下代码,去请求www.b.com域名的数据 <script> var script = document.creatElement('script'); script.type = 'text/javascript'; script.src = 'http://www.b.com/getdata?callback=demo'; function demo(res){ console.log(res); } </script>
这里,咱们利用动态脚本的src属性,变相地发送了一个http://www.b.com/getdata?call...。这时候,b.com页面接受到这个请求时,若是没有JSONP,会正常返回json的数据结果,像这样:{ msg: 'helloworld' },而利用JSONP,服务端会接受这个callback参数,而后用这个参数值包装要返回的数据:demo({msg: 'helloworld'});html5
这时候,若是a.com的页面上正好有一个demo 的函数:java
function demo(res){node
console.log(res);
}jquery
当远程数据一返回的时候,随着动态脚本的执行,这个demo函数就会被执行。nginx
$.ajax({ url:'http://www.b.com/getdata', type:'get', dataType: 'jsonp', // 请求方式为jsonp jsonpCallback: 'demo', // 自定义回调函数名 data: {} });
以nodejs为例web
var http = require(http); //引入url模块解析url字符串 var url = require('url); //引入querystring模块处理query字符串 var querystring = require('querystring'); var server = http.createServer(); server.on('request',function(req,res){ var urlPath = url.parse(req.url).pathname; var param = querystring .parse(req.url.split('?')[1]); if(urlPath === '/getData' && param.callback) { res.writeHead(200,{'Content-Type','application/json;charset=utf-8'}); var data = { msg: 'helloworld' }; data = JSON.stringify(data ); var callback = param .callback+'('+data+');'; res.write(callback); res.end(); } else { res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'}); res.write('Hell World\n'); res.end(); } })
jsonp缺点:只能使用get请求,不推荐使用
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不一样源服务器上的指定的资源。当一个资源从与该资源自己所在的服务器不一样的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。Cross-Origin Resource Sharing跨域资源共享,应该算是如今比较推荐的跨域处理方案.不只适用于各类Method,并且更加方便和简单
目前,全部浏览器都支持该功能,IE浏览器不能低于IE10。
浏览器将CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求同时知足如下条件,只要不知足如下条件的则为非简单请求
非简单请求会发出一次预检测请求,返回码是204,预检测经过才会真正发出请求,这才返回200。这里经过前端发请求的时候增长一个额外的headers来触发非简单请求。
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。这是由于请求的首部中携带了 Cookie 信息,若是 Access-Control-Allow-Origin
的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为
http://www.a.com,则请求将成功执行。
var express=require('express'); var url=require('url'); var app=express(); var allowCrossDomain = function(req, res, next) { res.header('Access-Control-Allow-Origin', 'http://localhost:63342'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Credentials','true'); next(); }; app.use(allowCrossDomain); app.get('/getData',function (req,res,next) { var queryValue=url.parse(req.url).query; if(queryValue==='fortunewheel@sina.com'){ res.send(true); }else { res.send(false); } }); app.listen(3001);
实际开发过程当中,为了安全,会和token一块儿使用
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数很少能够跨域操做的window属性之一,它可用于解决如下方面的问题:
用法:
postMessage(data,origin)方法接受两个参数,
data:须要传递的数据,html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,因此传参时最好用JSON.stringify()序列化。
origin:协议+主机+端口号,也能够设置为"*",表示能够传递给任意窗口,若是要指定和当前窗口同源的话设置为"/"。
<iframe id="iframe" src="http://www.b.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'jianjian' }; // 向http://www.b.com传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.b.com'); }; // 接受http://www.b.com返回数据 window.addEventListener('message', function(e) { alert('data from http://www.b.com---> ' + e.data); }, false); </script>
<script> // 接收http://www.a.com/a.html的数据 window.addEventListener('message', function(e) { alert('data from http://www.a.com/a.html---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 处理后再发回http://www.a.com/a.html window.parent.postMessage(JSON.stringify(data), 'http://www.a.com'); } }, false); </script>
这种方式只适合主域名相同,但子域名不一样的iframe跨域。
实现原理:两个页面都经过js强制设置document.domain为基础主域,就实现了同域。
<iframe id="iframe" src="http://www.child.a.com/b.html" style="display:none;"></iframe> <script> document.domain = 'a.com'; var a = 'hello world'; </script>
"http://www.child.a.com/b.html
<script> document.domain = 'a.com'; var b = window.parent.a; console.log(b); </script>
window.name 传输技术的基本原理:
当在浏览器中打开一个页面,或者在页面中添加一个iframe时即会建立一个对应的window对象,当页面加载另外一个新的页面时,window.name的属性是不会变的。这样就能够利用在页面动态添加一个iframe而后加载数据页面,在数据页面将须要的数据赋值给window.name。然而此时承载的iframe的parent页面仍是不能直接访问不在同一域下的iframe的那么属性,这时,只须要将iframe再加载一个与承载页面同域的空白页面,便可对window.name进行数据读取。
经过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操做。
具体实现:
http://www.a.com/a.html 主页面
http://www.b.com/b.html 数据页面
http://www.a.com/proxy.html 代理页面
<script> function crosDomainGetData(url,callback){ var state = 0; var iframe = document.createElement('iframe); iframe.src = url; iframe.onload = function(){ if(state === 1){ //代理页面成功事后,读取window.name var data = iframe.contentWindow.name; callback&&callback(data); //销毁iframe iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else { //第一次加载数据页面成功后,切换代理页面 state = 1; iframe.contentWindow.location = 'http://www.a.com/proxy.html'; } } document.body.appendChild(iframe); } crosDomainGetData('http://www.b.com/b.html',function(data){ alert(data); }) </script>
window.name = '123'
server{ # 监听8080端口 listen 8080; # 域名是localhost server_name localhost; #凡是localhost:8080/api这个样子的,都转发到真正的服务端地址http://www.b.com:8080 location ^~ /api { proxy_pass http://www.b.com:8080; } }
配置以后就不须要前端作什么修改了,通常咱们在先后端分离项目中开发阶段会采用这种方式,但不是全部场景都能这样作,例如后端接口是一个公共的API,好比一些公共服务获取天气什么的。
websoket协议自然支持跨域,你只须要学会如何使用它便可,关于websocket协议请看个人另一篇文章WebSocket网络通讯协议
参考文章:
https://developer.mozilla.org...
https://segmentfault.com/a/11...
原文地址:https://segmentfault.com/a/1190000017312269