首页 » 网站建设 » php跨域上传技巧_web跨域总结

php跨域上传技巧_web跨域总结

访客 2024-11-03 0

扫一扫用手机浏览

文章目录 [+]

同源策略限定了一下行为:

Cookie、LocalStorage 和 IndexDB 无法读取DOM 和 JS 工具无法获取Ajax要求发送不出去常见的跨域场景

所谓的同源是指,域名、协议、端口均为相同。

php跨域上传技巧_web跨域总结

http://www.nealyang.cn/index.html调用http://www.nealyang.cn/server.php非跨域http://www.nealyang.cn/index.html调用http://www.neal.cn/server.php跨域,主域不同http://abc.nealyang.cn/index.html调用http://def.neal.cn/server.php跨域,子域名不同http://www.nealyang.cn:8080/index.html调用http://www.nealyang.cn/server.php跨域,端口不同https://www.nealyang.cn/index.html调用http://www.nealyang.cn/server.php跨域,协议不同localhost调用127.0.0.1跨域跨域的办理办法jsonp跨域

jsonp跨域实在也是JavaScript设计模式中的一种代理模式。
在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器许可的,以是我们可以通过这个“犯罪漏洞”来进行跨域。
一样平常,我们可以动态的创建script标签,再去要求一个带参网址来实现跨域通信

php跨域上传技巧_web跨域总结
(图片来自网络侵删)

//原生的实现办法letscript=document.createElement('script');script.src='http://www.nealyang.cn/login?username=Nealyang&callback=callback';document.body.appendChild(script);functioncallback(res){console.log(res);}

当然,jquery也支持jsonp的实现办法

$.ajax({url:'http://www.nealyang.cn/login',type:'GET',dataType:'jsonp',//要求办法为jsonpjsonpCallback:'callback',data:{"username":"Nealyang"}})

虽然这种办法非常好用,但是一个最大的毛病是,只能够实现get要求

document.domain + iframe 跨域

这种跨域的办法最紧张的是哀求主域名相同。
什么是主域名相同呢?www.nealyang.cn aaa.nealyang.cn ba.ad.nealyang.cn 这三个主域名都是nealyang.cn,而主域名不同的就不能用此方法。

假设目前a.nealyang.cn 和 b.nealyang.cn 分别对应指向不同ip的做事器。

a.nealyang.cn 下有一个test.html文件

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>html</title><scripttype="text/javascript"src="jquery-1.12.1.js"></script></head><body><div>A页面</div><iframestyle="display:none"name="iframe1"id="iframe"src="http://b.nealyang.cn/1.html"frameborder="0"></iframe><scripttype="text/javascript">$(function(){try{document.domain="nealyang.cn"}catch(e){}$("#iframe").load(function(){varjq=document.getElementById('iframe').contentWindow.$jq.get("http://nealyang.cn/test.json",function(data){console.log(data);});})})</script></body></html>

利用 iframe 加载 其他域下的文件(nealyang.cn/1.html), 同时 document.domain 设置成 nealyang.cn ,当 iframe 加载完毕后就可以获取 nealyang.cn 域下的全局工具,此时考试测验着去要求 nealyang.cn 域名下的 test.json (此时可以要求接口),就会创造数据要求失落败了~~ 惊不惊喜,意不虞外!






数据要求失落败,目的没有达到,自然是还少一步:

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>html</title><scripttype="text/javascript"src="jquery-1.12.1.js"></script><scripttype="text/javascript">$(function(){try{document.domain="nealyang.com"}catch(e){}})</script></head><body><divid="div1">B页面</div></body></html>

此时在进行刷新浏览器,就会创造数据这次真的是成功了~

window.name + iframe 跨域

window.name属性可设置或者返回存放窗口名称的一个字符串。
他的神奇之处在于name值在不同页面或者不同域下加载后依旧存在,没有修正就不会发生变革,并且可以存储非常长的name(2MB)

假设index页面要求远端做事器上的数据,我们在该页面下创建iframe标签,该iframe的src指向做事器文件的地址(iframe标签src可以跨域),做事器文件里设置好window.name的值,然后再在index.html里面读取改iframe中的window.name的值。
完美~

<body><scripttype="text/javascript">iframe=document.createElement('iframe'),iframe.src='http://localhost:8080/data.php';document.body.appendChild(iframe);iframe.onload=function(){console.log(iframe.contentWindow.name)};</script></body>

当然,这样还是不足的。

由于规定如果index.html页面和和该页面里的iframe框架的src如果不同源,则也无法操作框架里的任何东西,以是就取不到iframe框架的name值了,见告你我们不是一家的,你也休想得到我这里的数据。
既然要同源,那就换个src去指,前面说了无论若何加载window.name值都不会变革,于是我们在index.html相同目录下,新建了个proxy.html的空页面,修正代码如下:

<body><scripttype="text/javascript">iframe=document.createElement('iframe'),iframe.src='http://localhost:8080/data.php';document.body.appendChild(iframe);iframe.onload=function(){iframe.src='http://localhost:81/cross-domain/proxy.html';console.log(iframe.contentWindow.name)};</script></body>

空想彷佛很美好,在iframe载入过程中,迅速重置iframe.src的指向,使之与index.html同源,那么index页面就能去获取它的name值了!
但是现实是残酷的,iframe在现实中的表现是一贯一直地刷新,也很好理解,每次触发onload事宜后,重置src,相称于重新载入页面,又触发onload事宜,于是就一直地刷新了(但是须要的数据还是能输出的)。
修正后代码如下:

<body><scripttype="text/javascript">iframe=document.createElement('iframe');iframe.style.display='none';varstate=0;iframe.onload=function(){if(state===1){vardata=JSON.parse(iframe.contentWindow.name);console.log(data);iframe.contentWindow.document.write('');iframe.contentWindow.close();document.body.removeChild(iframe);}elseif(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></body>

以是如上,我们就拿到了做事器返回的数据,但是有几个条件是必不可少的:

iframe标签的跨域能力window.names属性值在文档刷新后依然存在的能力location.hash + iframe 跨域

此跨域方法和上面先容的比较类似,一样是动态插入一个iframe然后设置其src为做事端地址,而做事端同样输出一端js代码,也同时通过与子窗口之间的通信来完成数据的传输。

关于锚点相信大家都已经知道了,实在便是设置锚点,让文档指定的相应的位置。
锚点的设置用a标签,然后href指向要跳转到的id,当然,条件是你得有个滚动条,不然也不好滚动嘛是吧。

而location.hash实在便是url的锚点。
比如http://www.nealyang.cn#Nealyang的网址打开后,在掌握台输入location.hash就会返回#Nealyang的字段。

根本知识补充完毕,下面我们来说下如何实现跨域

如果index页面要获取远端做事器的数据,动态的插入一个iframe,将iframe的src实行做事器的地址,这时候的top window 和包裹这个iframe的子窗口是不能通信的,由于同源策略,以是改变子窗口的路径就可以了,将数据当做改变后的路径的hash值加载路径上,然后就可以通信了。
将数据加在index页面地址的hash上,index页面监听hash的变革,h5的hashchange方法

<body><scripttype="text/javascript">functiongetData(url,fn){variframe=document.createElement('iframe');iframe.style.display='none';iframe.src=url;iframe.onload=function(){fn(iframe.contentWindow.location.hash.substring(1));window.location.hash='';document.body.removeChild(iframe);};document.body.appendChild(iframe);}//getdatafromservervarurl='http://localhost:8080/data.php';getData(url,function(data){varjsondata=JSON.parse(data);console.log(jsondata.name+''+jsondata.age);});</script></body>

补充解释:实在location.hash和window.name都是差不多的,都是利用全局工具属性的方法,然后这两种方法和jsonp也是一样的,便是只能够实现get要求

postMessage跨域

这是由H5提出来的一个炫酷的API,IE8+,chrome,ff都已经支持实现了这个功能。
这个功能也是非常的大略,个中包括接管信息的Message韶光,和发送信息的postMessage方法。

发送信息的postMessage方法是向外界窗口发送信息

otherWindow.postMessage(message,targetOrigin);

otherWindow指的是目标窗口,也便是要给哪一个window发送,是window.frames属性的成员或者是window.open方法创建的窗口。
Message是要发送的,类型为String,Object(IE8、9不支持Obj),targetOrigin是限定接管范围,不限定就用星号

接管信息的message事宜

varonmessage=function(event){vardata=event.data;varorigin=event.origin;}if(typeofwindow.addEventListener!='undefined'){window.addEventListener('message',onmessage,false);}elseif(typeofwindow.attachEvent!='undefined'){window.attachEvent('onmessage',onmessage);}

举个例子

a.html(http://www.nealyang.cn/a.html)

<iframeid="iframe"src="http://www.neal.cn/b.html"style="display:none;"></iframe><script>variframe=document.getElementById('iframe');iframe.onload=function(){vardata={name:'aym'};//向neal传送跨域数据iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.neal.cn');};//接管domain2返回数据window.addEventListener('message',function(e){alert('datafromneal--->'+e.data);},false);</script>

b.html(http://www.neal.cn/b.html)

<script>//吸收domain1的数据window.addEventListener('message',function(e){alert('datafromnealyang--->'+e.data);vardata=JSON.parse(e.data);if(data){data.number=16;//处理后再发回nealyangwindow.parent.postMessage(JSON.stringify(data),'http://www.nealyang.cn');}},false);</script>跨域资源共享 CORS

由于是目前主流的跨域办理方案。
以是这里多先容点。

简介

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它许可浏览器向跨源做事器,发出XMLHttpRequest要求,从而战胜了AJAX只能同源利用的限定。

CORS须要浏览器和做事器同时支持。
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
IE8+:IE8/9须要利用XDomainRequest工具来支持CORS。

全体CORS通信过程,都是浏览器自动完成,不须要用户参与。
对付开拓者来说,CORS通信与同源的AJAX通信没有差别,代码完备一样。
浏览器一旦创造AJAX要求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的要求,但用户不会有觉得。
因此,实现CORS通信的关键是做事器。
只要做事器实现了CORS接口,就可以跨源通信。

两种要求

提及来很搞笑,分为两种要求,一种是大略要求,另一种是非大略要求。
只要知足下面条件便是大略要求

要求办法为HEAD、POST 或者 GEThttp头信息不超出以下字段:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)

为什么要分为大略要乞降非大略要求,由于浏览器对这两种要求办法的处理办法是不同的。

大略要求基本流程

对付大略要求,浏览器直接发出CORS要求。
详细来说,便是在头信息之中,增加一个Origin字段。
下面是一个例子,浏览器创造这次跨源AJAX要求是大略要求,就自动在头信息之中,添加一个Origin字段。

GET/corsHTTP/1.1Origin:http://api.bob.comHost:api.alice.comAccept-Language:en-USConnection:keep-aliveUser-Agent:Mozilla/5.0...

Origin字段用来解释,本次要求来自哪个源(协议 + 域名 + 端口)。
做事器根据这个值,决定是否赞许这次要求。

如果Origin指定的源,不在容许范围内,做事器会返回一个正常的HTTP回应。
浏览器创造,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个缺点,被XMLHttpRequest的onerror回调函数捕获。

把稳,这种缺点无法通过状态码识别,由于HTTP回应的状态码有可能是200。

如果Origin指定的域名在容许范围内,做事器返回的相应,会多出几个头信息字段。

Access-Control-Allow-Origin:http://api.bob.comAccess-Control-Allow-Credentials:trueAccess-Control-Expose-Headers:FooBarContent-Type:text/html;charset=utf-8

上面的头信息之中,有三个与CORS要求干系的字段,都以Access-Control-开头

Access-Control-Allow-Origin :该字段是必须的。
它的值要么是要求时Origin字段的值,要么是一个,表示接管任意域名的要求Access-Control-Allow-Credentials: 该字段可选。
它的值是一个布尔值,表示是否许可发送Cookie。
默认情形下,Cookie不包括在CORS要求之中。
设为true,即表示做事器明确容许,Cookie可以包含在要求中,一起发给做事器。
这个值也只能设为true,如果做事器不要浏览器发送Cookie,删除该字段即可。
Access-Control-Expose-Headers:该字段可选。
CORS要求时,XMLHttpRequest工具的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
withCredentials 属性

上面说到,CORS要求默认不发送Cookie和HTTP认证信息。
如果要把Cookie发到做事器,一方面要做事器赞许,指定Access-Control-Allow-Credentials字段。

另一方面,开拓者必须在AJAX要求中打开withCredentials属性。

varxhr=newXMLHttpRequest();//IE8/9需用window.XDomainRequest兼容//前端设置是否带cookiexhr.withCredentials=true;xhr.open('post','http://www.domain2.com:8080/login',true);xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xhr.send('user=admin');xhr.onreadystatechange=function(){if(xhr.readyState==4&&xhr.status==200){alert(xhr.responseText);}};//jquery$.ajax({...xhrFields:{withCredentials:true//前端设置是否带cookie},crossDomain:true,//会让要求头中包含跨域的额外信息,但不会含cookie...});

否则,纵然做事器赞许发送Cookie,浏览器也不会发送。
或者,做事器哀求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。
这时,可以显式关闭withCredentials。

须要把稳的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与要求网页同等的域名。
同时,Cookie依然遵照同源政策,只有用做事器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取做事器域名下的Cookie。

非大略要求

非大略要求是那种对做事器有分外哀求的要求,比如要求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非大略要求的CORS要求,会在正式通信之前,增加一次HTTP查询要求,称为"预检"要求(preflight)。

浏览器先讯问做事器,当前网页所在的域名是否在做事器的容许名单之中,以及可以利用哪些HTTP动词和头信息字段。
只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest要求,否则就报错。

varurl='http://api.alice.com/cors';varxhr=newXMLHttpRequest();xhr.open('PUT',url,true);xhr.setRequestHeader('X-Custom-Header','value');xhr.send();

浏览器创造,这是一个非大略要求,就自动发出一个"预检"要求,哀求做事器确认可以这样要求。
下面是这个"预检"要求的HTTP头信息。

OPTIONS/corsHTTP/1.1Origin:http://api.bob.comAccess-Control-Request-Method:PUTAccess-Control-Request-Headers:X-Custom-HeaderHost:api.alice.comAccept-Language:en-USConnection:keep-aliveUser-Agent:Mozilla/5.0...

"预检"要求用的要求方法是OPTIONS,表示这个要求是用来讯问的。
头信息里面,关键字段是Origin,表示要求来自哪个源。

除了Origin字段,"预检"要求的头信息包括两个分外字段。

Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS要求会用到哪些HTTP方法,上例是PUT。
Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS要求会额外发送的头信息字段,上例是X-Custom-Header预检要求的回应

做事器收到"预检"要求往后,检讨了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段往后,确认许可跨源要求,就可以做出回应

HTTP/1.1200OKDate:Mon,01Dec200801:15:39GMTServer:Apache/2.0.61(Unix)Access-Control-Allow-Origin:http://api.bob.comAccess-Control-Allow-Methods:GET,POST,PUTAccess-Control-Allow-Headers:X-Custom-HeaderContent-Type:text/html;charset=utf-8Content-Encoding:gzipContent-Length:0Keep-Alive:timeout=2,max=100Connection:Keep-AliveContent-Type:text/plain

上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以要求数据。
该字段也可以设为星号,表示赞许任意跨源要求。

如果浏览器否定了"预检"要求,会返回一个正常的HTTP回应,但是没有任何CORS干系的头信息字段。
这时,浏览器就会认定,做事器不同意预检要求,因此触发一个缺点,被XMLHttpRequest工具的onerror回调函数捕获。
掌握台会打印出如下的报错信息。

做事器回应的其他CORS干系字段如下:

Access-Control-Allow-Methods:GET,POST,PUTAccess-Control-Allow-Headers:X-Custom-HeaderAccess-Control-Allow-Credentials:trueAccess-Control-Max-Age:1728000Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明做事器支持的所有跨域要求的方法。
把稳,返回的是所有支持的方法,而不单是浏览器要求的那个方法。
这是为了避免多次"预检"要求。
Access-Control-Allow-Headers:如果浏览器要求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。
它也是一个逗号分隔的字符串,表明做事器支持的所有头信息字段,不限于浏览器在"预检"中要求的字段。
Access-Control-Allow-Credentials:该字段与大略要求时的含义相同。
Access-Control-Max-Age:该字段可选,用来指定本次预检要求的有效期,单位为秒。
上面结果中,有效期是20天(1728000秒),即许可缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检要求。
浏览器正常要求回应

一旦做事器通过了"预检"要求,往后每次浏览器正常的CORS要求,就都跟大略要求一样,会有一个Origin头信息字段。
做事器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

PUT/corsHTTP/1.1Origin:http://api.bob.comHost:api.alice.comX-Custom-Header:valueAccept-Language:en-USConnection:keep-aliveUser-Agent:Mozilla/5.0...

浏览器的正常CORS要求。
上面头信息的Origin字段是浏览器自动添加的。
下面是做事器正常的回应。

Access-Control-Allow-Origin:http://api.bob.comContent-Type:text/html;charset=utf-8

Access-Control-Allow-Origin字段是每次回应都必定包含的

结束语

CORS与JSONP的利用目的相同,但是比JSONP更强大。
JSONP只支持GET要求,CORS支持所有类型的HTTP要求。
JSONP的上风在于支持老式浏览器,以及可以向不支持CORS的网站要求数据。

WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。
它实现了浏览器与做事器全双工通信,同时许可跨域通讯,是server push技能的一种很好的实现。

原生WebSocket API利用起来不太方便,我们利用Socket.io,它很好地封装了webSocket接口,供应了更大略、灵巧的接口,也对不支持webSocket的浏览器供应了向下兼容。

前端代码:

<div>user input:<inputtype="text"></div><scriptsrc="./socket.io.js"></script><script>varsocket=io('http://www.domain2.com:8080');//连接成功处理socket.on('connect',function(){//监听做事端socket.on('message',function(msg){console.log('datafromserver:--->'+msg);});//监听做事端关闭socket.on('disconnect',function(){console.log('Serversockethasclosed.');});});document.getElementsByTagName('input')[0].onblur=function(){socket.send(this.value);};</script>

node Server

varhttp=require('http');varsocket=require('socket.io');//启http做事varserver=http.createServer(function(req,res){res.writeHead(200,{'Content-type':'text/html'});res.end();});server.listen('8080');console.log('Serverisrunningatport8080...');//监听socket连接socket.listen(server).on('connection',function(client){//吸收信息client.on('message',function(msg){ client.send('hello:'+ msg);console.log('datafromclient:--->'+msg);});//断开处理client.on('disconnect',function(){console.log('Clientsockethasclosed.');});});node代理跨域

node中间件实现跨域代理,是通过启一个代理做事器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修正相应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

利用node + express + http-proxy-middleware搭建一个proxy做事器

前端代码

varxhr=newXMLHttpRequest();//前端开关:浏览器是否读写cookiexhr.withCredentials=true;//访问http-proxy-middleware代理做事器xhr.open('get','http://www.domain1.com:3000/login?user=admin',true);xhr.send();

后端代码

varexpress=require('express');varproxy=require('http-proxy-middleware');varapp=express();app.use('/',proxy({//代理跨域目标接口target:'http://www.domain2.com:8080',changeOrigin:true,//修正相应头信息,实现跨域并许可带cookieonProxyRes:function(proxyRes,req,res){res.header('Access-Control-Allow-Origin','http://www.domain1.com');res.header('Access-Control-Allow-Credentials','true');},//修正相应信息中的cookie域名cookieDomainRewrite:'www.domain1.com'//可以为false,表示不修正}));app.listen(3000);console.log('Proxyserverislistenatport3000...');nginx代理跨域

NGINX实在个人玩的不是太多,以是这里不能误人子弟。
大略的说,咱们可以通过 Nginx 转发要求,把跨域的接口写成调本域的接口,然后将这些接口转发到真正的要求地址上。
详细 Nginx 的配置解释,可以参看后续我整理的 Nginx 教程解释。

参考文档

http://www.ruanyifeng.com/blog/2016/04/cors.htmlhttps://segmentfault.com/a/1190000011145364

标签:

相关文章

同步与异步加载,SEO优化中的关键步骤

网站数量日益增多,如何在众多网站中脱颖而出,成为SEO优化的重要课题。同步与异步加载作为SEO优化中的关键策略,对于提升网站性能、...

网站建设 2025-03-31 阅读1 评论0