同源是指相同的协议、域名、端口,三者都相同才属于同域。不符合上述定义的请求,则称为跨域。
相信每个开发人员都曾遇到过跨域请求的情况,虽然情况不一样,但问题的本质都可以归为浏览器出于安全考虑下的同源策略的限制。
跨域的情形有很多,最常见的有Ajax跨域、Socket跨域和Canvas跨域。下面列举一些我们常见的跨域情形下,某些浏览器控制台给出的错误提示:
FireFox下的提示:
已阻止交叉源请求:同源策略不允许读取***上的远程资源。可以将资源移动到相同的域名上或者启用 CORS 来解决这个问题。
Canvas跨域Chrome下的提示:
UncaughtSecurityError:Failed to execute'getImageData' on 'CanvasRenderingContext2D':The canvas has been taintedby cross-origin data.
或:
Imagefrom origin 'http://js.xx.com' has been blocked from loading by Cross-OriginResource Sharing policy: No 'Access-Control-Allow-Origin' header is present onthe requested resource. Origin 'http://act.xx.com' is therefore not allowedaccess.
1)document.domain+iframe的设置
2)动态创建script
3)利用iframe和location.hash
4)window.name实现的跨域数据传输
5)使用HTML5 postMessage
6)利用flash
7)通过代理,js访问代理,代理转到不同的域
http://developer.yahoo.com/javascript/howto-proxy.html
8)jQuery JSONP(不能成为真正的Ajax,本质上仍是动态创建script)
http://www.cnblogs.com/chopper/archive/2012/03/24/2403945.html
9)跨域资源共享(CORS) 这是HTML5跨域问题的标准解决方案
说明:方案1~方案6见Rain Man所写的文章《JavaScript跨域总结与解决办法》
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html
1) 绕开跨域。
适用情形是:动静分离。
example1.com域名下的页面中跨域请求是以JavaScript内联方式实现的,而请求的目标静态资源是放在example2.com域名下,这时可以将执行跨域请求的JavaScript代码块独立出来,放到example2.com上,而example1.com页面通过外联方式引入该静态域名下的js文件。这样,js与请求的图片等资源都在example2.com下,即可正常访问到。这种方法其实是一种巧妙避开跨域的方法。
适用情形:动静不分离(两个域名均运行访问静态资源)。
example1.com请求example2.com下的资源图片,可以使用PHP抓取图片并在example2.com下生成一份,这样就可以间接访问到example1.com的静态资源。
html模板示例代码:
$("#scratchpad").wScratchPad({ //刮刮卡示例,当前域名http://act.xxx.com
width:283,
height:154,
//color: "#a9a9a7",
image2:"imgdata.PHP?url=http://js.xxx.com/static/activity/sq/guagua315/images/card_inner.jpg",
scratchMove:function() {
}
});
或:
xi=newXMLHttpRequest();
xi.open("GET","imgdata.PHP?url="+yourImageURL,true);
xi.send();
xi.onreadystatechange=function() {
if(xi.readyState==4 && xi.status==200) {
img=newImage;
img.onload=function(){
ctx.drawImage(img, 0,0,canvas.width,canvas.height);
}
img.src=xi.responseText;
}
$url=$_GET['url'];
$img =file_get_contents($url);
$imgname = substr(strrchr($url,"/"),1);
file_put_contents($fn,$img);
echo $imgname;
?>
3) 后台程序设置Access-Control-Allow-Origin
适用情形:Ajax获取跨域接口的JSON数据。
example1.com请求example2.com的数据接口,则需要在example2.com的数据接口添加跨域访问授权。
PHP程序中开始出添加header('HeaderName:HeaderValue'); 这样的header标记:
header('Access-Control-Allow-Origin:*');
4)修改服务器配置启用CORS
适用情形:跨域访问静态资源。
Access-Control-Allow-Origin是什么作用呢?用于授权资源的跨站访问。比如,静态资源图片都放在example2.com 域名下,如果在返回的头中没有设置 Access-Control-Allow-Origin,那么别的域是没有权限外链你的图片的。
要实现CORS跨域,服务端需要这个一个流程,图片引自html5rocks,附图如下
a.对于简单请求,如GET,只需要在HTTP Response后添加Access-Control-Allow-Origin。
b.对于非简单请求,比如POST、PUT、DELETE等,浏览器会分两次应答。第一次preflight(method: OPTIONS),主要验证来源是否合法,并返回允许的Header等。第二次才是真正的HTTP应答。所以服务器必须处理OPTIONS应答。
这里是一个Nginx启用CORS的参考配置示例http://enable-cors.org/server_nginx.html。代码:
- #
- #ACORS(Cross-OriginResouceSharing)configforNginx
- #
- #==Purpose
- #
- #ThisNginxconfigurationenablesCORSrequestsinthefollowingway:
- #-enablesCORSjustfororiginsonawhitelistspecifiedbyaregularexpression
- #-CORSpreflightrequest(OPTIONS)arerespondedimmediately
- #-Access-Control-Allow-Credentials=trueforGETandPOSTrequests
- #-Access-Control-Max-Age=20days,tominimizerepetitiveOPTIONSrequests
- #-varIoUssuperluoussettingstoaccommodatenonconformantbrowsers
- #
- #==CommentonechoingAccess-Control-Allow-Origin
- #
- #HowdoyouallowCORSrequestsonlyfromcertaindomains?Thelast
- #publishedW3Ccandidaterecommendationstatesthatthe
- #Access-Control-Allow-Originheadercanincludealistoforigins.
- #(See:http://www.w3.org/TR/2013/CR-cors-20130129/#access-control-allow-origin-response-header)
- #However,browsersdonotsupportthiswellanditlikelywillbe
- #droppedfromthespec(see,http://www.rfc-editor.org/errata_search.PHP?rfc=6454&eid=3249).
- #
- #Theusualworkaroundisfortheservertokeepawhitelistof
- #acceptableorigins(asaregularexpression),matchtherequest's
- #Originheaderagainstthelist,andechobackthematchedvalue.
- #
- #(Yesyoucanuse'*'toacceptalloriginsbutthisistooopenand
- #preventsusing'Access-Control-Allow-Credentials:true',whichis
- #neededforHTTPBasicAccessauthentication.)
- #
- #==Commentonspec
- #
- #CommentsbelowareallbasedonmyreadingoftheCORSSpecasof
- #2013-Jan-29(http://www.w3.org/TR/2013/CR-cors-20130129/),the
- #XMLHttpRequestspec(
- #http://www.w3.org/TR/2012/WD-XMLHttpRequest-20121206/),and
- #experimentationwithlatestversionsofFirefox,Chrome,Safariat
- #thatpointintime.
- #
- #==Changelog
- #
- #sharedat:https://gist.github.com/algal/5480916
- #basedon:https://gist.github.com/alexjs/4165271
- #
- location/{
- #iftherequestincludedanOrigin:headerwithanoriginonthewhitelist,
- #thenitissomekindofCORSrequest.
- #specifically,thisexampleallowCORSrequestsfrom
- #scheme:httporhttps
- #authority:anyauthorityendingin".mckinsey.com"
- #port:nothing,or:
- if($http_origin~*(https?://[^/]*\.mckinsey\.com(:[0-9]+)?)$){
- set$cors"true";
- }
- #Nginxdoesn'tsupportnestedIfstatements,soweusestring
- #concatenationtocreateaflagforcompoundconditions
- #OPTIONSindicatesaCORSpre-flightrequest
- if($request_method='OPTIONS'){
- set$cors"${cors}options";
- }
- #non-OPTIONSindicatesanormalCORSrequest
- if($request_method='GET'){
- set$cors"${cors}get";
- }
- if($request_method='POST'){
- set$cors"${cors}post";
- }
- #ifit'saGETorPOST,setthestandardCORSresponsesheader
- if($cors="trueget"){
- #Tellsthebrowserthisoriginmaymakecross-originrequests
- #(Here,weechotherequestingorigin,whichmatchedthewhitelist.)
- add_header'Access-Control-Allow-Origin'"$http_origin";
- #Tellsthebrowseritmayshowtheresponse,whenXmlHttpRequest.withCredentials=true.
- add_header'Access-Control-Allow-Credentials''true';
- ##TellthebrowserwhichresponseheaderstheJScansee,besidesthe"simpleresponseheaders"
- #add_header'Access-Control-Expose-Headers''myresponseheader';
- }
- if($cors="truepost"){
- #Tellsthebrowserthisoriginmaymakecross-originrequests
- #(Here,besidesthe"simpleresponseheaders"
- #add_header'Access-Control-Expose-Headers''myresponseheader';
- }
- #ifit'sOPTIONS,thenit'saCORSpreflightrequestsorespondimmediatelywithnoresponsebody
- if($cors="trueoptions"){
- #Tellsthebrowserthisoriginmaymakecross-originrequests
- #(Here,whichmatchedthewhitelist.)
- add_header'Access-Control-Allow-Origin'"$http_origin";
- #inapreflightresponse,tellsbrowserthesubsequentactualrequestcanincludeusercredentials(e.g.,cookies)
- add_header'Access-Control-Allow-Credentials''true';
- #
- #Returnspecialpreflightinfo
- #
- #Tellbrowsertocachethispre-flightinfofor20days
- add_header'Access-Control-Max-Age'1728000;
- #TellbrowserwerespondtoGET,POST,OPTIONSinnormalCORSrequests.
- #
- #Notofficiallyneededbutstillincludedtohelpnon-conformingbrowsers.
- #
- #OPTIONSshouldnotbeneededhere,sincethefieldisused
- #toindicatemethodsallowedfor"actualrequest"notthe
- #preflightrequest.
- #
- #GET,POSTalsoshouldnotbeneeded,sincethe"simple
- #methods"GET,HEADareincludedbydefault.
- #
- #Weshouldonlyneedthisheaderfornon-simplerequests
- #methods(e.g.,DELETE),orcustomrequestmethods(e.g.,XMODIFY)
- add_header'Access-Control-Allow-Methods''GET,OPTIONS';
- #Tellbrowserweaccepttheseheadersintheactualrequest
- #
- #Adynamic,wide-openconfigwouldjustechobackalltheheaders
- #listedinthepreflightrequest's
- #Access-Control-Request-Headers.
- #
- #Adynamic,restrictiveconfig,wouldjustechobackthe
- #subsetofAccess-Control-Request-Headersheaderswhichare
- #allowedforthisresource.
- #
- #Thisstatic,fairlyopenconfigjustreturnsahardcodedsetof
- #headersthatcoversmanycases,includingsomeheadersthat
- #areofficiallyunnecessarybutactuallyneededtosupport
- #non-conformingbrowsers
- #
- #Commentonsomeparticularheadersbelow:
- #
- #Authorization--practicallyandofficiallyneededtosupport
- #requestsusingHTTPBasicAccessauthentication.BrowserJS
- #canuseHTTPBAauthenticationwithanXmlHttpRequestobject
- #reqbycalling
- #
- #req.withCredentials=true,and
- #req.setRequestHeader('Authorization','Basic'+window.btoa(theusername+':'+thepassword))
- #
- #Counterintuitively,theusernameandpasswordfieldson
- #XmlHttpRequest#opencannotbeusedtosettheauthorization
- #fieldautomaticallyforCORSrequests.
- #
- #Content-Type--thisisa"simpleheader"onlywhenit's
- #valueiseitherapplication/x-www-form-urlencoded,
- #multipart/form-data,ortext/plain;andinthatcaseitdoes
- #notofficiallyneedtobeincluded.But,ifyourbrowser
- #codesetsthecontenttypeasapplication/json,forexample,
- #thenthatmakestheheadernon-simple,andthenyourserver
- #mustdeclarethatitallowstheContent-Typeheader.
- #
- #Accept,Accept-Language,Content-Language--thesearethe
- #"simpleheaders"andtheyareofficiallynever
- #required.Practically,possiblyrequired.
- #
- #Origin--logically,shouldnotneedtobeexplicitly
- #required,sinceit'simplicitlyrequiredbyallof
- #CORS.officially,itisunclearifitisrequiredor
- #forbidden!practically,probablyrequiredbyexisting
- #browsers(GeckodoesnotrequestitbutWebKitdoes,so
- #WebKitmightchokeifit'snotreturnedback).
- #
- #User-Agent,DNT--officially,shouldnotberequired,as
- #theycannotbesetas"authorrequestheaders".practically,
- #mayberequired.
- #
- #MyComment:
- #
- #Thespecsarecontradictory,orelsejustconfusingtome,
- #inhowtheydescribecertainheadersasrequiredbyCORSbut
- #forbiddenbyXmlHttpRequest.TheCORSSpecsaysthebrowser
- #issupposedtosetAccess-Control-Request-Headerstoinclude
- #only"authorrequestheaders"(section7.1.5).Andthenthe
- #serverissupposedtouseAccess-Control-Allow-Headersto
- #echobackthesubsetofthosewhichisallowed,tellingthe
- #browserthatitshouldnotcontinueandperformtheactual
- #requestifitincludesadditionalheaders(section7.1.5,
- #step8).Sothisimpliesthebrowserclientcodemusttake
- #caretoincludeallnecessaryheadersasauthorrequest
- #headers.
- #
- #However,thespecforXmlHttpRequest#setRequestHeader
- #(section4.6.2)providesalonglistofheaderswhichthe
- #thebrowserclientcodeisforbiddentoset,includingfor
- #instanceOrigin,DNT(donottrack),User-Agent,etc..This
- #isunderstandable:theseareallheadersthatwewantthe
- #browseritselftocontrol,sothatmalicIoUsbrowserclient
- #codecannotspoofthemandforinstancepretendtobefroma
- #differentorigin,etc..
- #
- #ButifXmlHttpRequestforbidsthebrowserclientcodefrom
- #settingthese(aspertheXmlHttpRequestspec),thenthey
- #arenotauthorrequestheaders.Andiftheyarenotauthor
- #requestheaders,thenthebrowsershouldnotincludethemin
- #thepreflightrequest'sAccess-Control-Request-Headers.And
- #iftheyarenotincludedinAccess-Control-Request-Headers,
- #thentheyshouldnotbeechoedby
- #Access-Control-Allow-Headers.Andiftheyarenotechoedby
- #Access-Control-Allow-Headers,thenthebrowsershouldnot
- #continueandexecuteactualrequest.Sothisseemstoimply
- #thattheCORSandXmlHttpRequestspecsforbidcertain
- #widely-usedfieldsinCORSrequests,includingtheOrigin
- #field,whichtheyalsorequireforCORSrequests.
- #
- #Thebottomline:itseemsthereareheadersneededforthe
- #webandCORStowork,whichatthemomentyoushould
- #hard-codeintoAccess-Control-Allow-Headers,although
- #officialspecsimplythisshouldnotbenecessary.
- #
- add_header'Access-Control-Allow-Headers''Authorization,Content-Type,Accept,Origin,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
- #buildentireresponsetothepreflightrequest
- #nobodyinthisresponse
- add_header'Content-Length'0;
- #(shouldnotbenecessary,butincludedfornon-conformingbrowsers)
- add_header'Content-Type''text/plaincharset=UTF-8';
- #indicatesuccessfulreturnwithnocontent
- return204;
- }
- #--PUTYOURREGULARNginxCODEHERE--
- }
服务器解析流程如下:
a.首先查看http头部有无origin字段;
b.如果没有,或者不允许,直接当成普通请求处理,结束;
c.如果有并且是允许的,那么再看是否是preflight(method=OPTIONS);
d.如果是preflight,就返回Allow-Headers、Allow-Methods等,内容为空;
e.如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常内容。
若服务器为Nginx,可以 在 Nginx 的 conf 文件中加入以下内容:
- location/{
- add_headerAccess-Control-Allow-Origin*;
- }
若服务器为Apache,则可以按照如下配置:
- <IfModulemod_setenvif.c>
- <IfModulemod_headers.c>
- <FilesMatch"\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
- SetEnvIfOrigin":"IS_CORS
- HeadersetAccess-Control-Allow-Origin"*"env=IS_CORS
- </FilesMatch>
- </IfModule>
- </IfModule>
为安全起见,Access-Control-Allow-Origin也可设为特定域名的方式。
在HTML5中,有些HTML元素为CORS提供了支持,如img、video新增了crossOrigin属性,属性值可以为anonymous或use-credentials。比如,canvas绘图要用到跨域图片,在JavaScript中要设置img.crossOrigin="Anonymous";
- varimg=newImage,
- canvas=document.createElement("canvas"),
- ctx=canvas.getContext("2d"),
- src="http://example.com/image";//insertimageurlhere
- img.crossOrigin="Anonymous";
- img.onload=function(){
- canvas.width=img.width;
- canvas.height=img.height;
- ctx.drawImage(img,0);
- localStorage.setItem("savedImageData",canvas.toDataURL("image/png"));
- }
- img.src=src;
- //makesuretheloadeventfiresforcachedimagestoo
- if(img.complete||img.complete===undefined){
- img.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
- img.src=src;
- }
上述配置完成后,重启服务器,CORS启用。
然后我们再刷新页面,查询请求头的参数,可以发现多出一个:Access-Control-Allow-Origin:*
,到此证明服务器配置已经生效。同时我们的canvas绘图也可以正常使用了。
刷新页面返回请求响应结果后,HTTP Request Headers的内容:
Remote Address:222.132.18.xx:80
Request URL:http://js.xx.com/static/activity/sq/guagua315/images/card_inner.jpg
Request Method:GET
Status Code:200 OK
Request Headersview source
Accept:image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Host:js.xx.com
Origin:http://act.xx.com
Pragma:no-cache
RA-Sid:7CCAD53E-20140704-054839-03c57a-85faf2
RA-Ver:2.8.8
Referer:http://act.xx.com/sq/guagua315?uuid=46124642&fid=2&sign=xxx
User-Agent:Mozilla/5.0 (Windows NT 6.1;WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/40.0.2214.115Safari/537.36
Response Headersview source
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Connection:close
Content-Length:4010
Content-Type:image/jpeg
Date:Thu,12 Mar 2015 02:29:43 GMT
ETag:"54f7d1b4-faa"
Last-Modified:Thu,05 Mar 2015 03:47:00 GMT
Powered-By-ChinaCache:MISS fromCNC-WF-3-3X6
Powered-By-ChinaCache:MISS fromCNC-WF-3-3X5
Server:Tengine
Switch:FSCS附图:
参考文章:
CORS enabled image https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
CORS on Nginx http://enable-cors.org/server_nginx.html
Nginx CORS实现JS跨域 http://www.jb51.cc/article/p-xhzdhafc-bbp.html
转载请注明出处,文章来自于freshlover的CSDN空间《Ajax跨域、Json跨域、Socket跨域和Canvas跨域等同源策略限制的解决方法》