作者:清楓草塘
出處:https://blog.csdn.net/freshlover/article/details/44223467
同源是指相同的協議、域名、端口,三者都相同才屬於同域。不符合上述定義的請求,則稱爲跨域。
相信每個開發人員都曾遇到過跨域請求的情況,雖然情況不一樣,但問題的本質都可以歸爲瀏覽器出於安全考慮下的同源策略的限制。
跨域的情形有很多,最常見的有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下,即可正常訪問到。這種方法其實是一種巧妙避開跨域的方法。
2) 後臺抓取克隆圖片。
適用情形:動靜不分離(兩個域名均運行訪問靜態資源)。
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;
}
}
PHP處理代碼:
<?php//imgdata.php
$url=$_GET['url'];
$img =file_get_contents($url);
$imgname = substr(strrchr($url,"/"),1);
file_put_contents($fn,$img);
echo $imgname;
?>
上述代碼在當前php目錄下生成了克隆生成了一張圖片。
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。代碼:
#
# A CORS (Cross-Origin Resouce Sharing) config for nginx
#
# == Purpose
#
# This nginx configuration enables CORS requests in the following way:
# - enables CORS just for origins on a whitelist specified by a regular expression
# - CORS preflight request (OPTIONS) are responded immediately
# - Access-Control-Allow-Credentials=true for GET and POST requests
# - Access-Control-Max-Age=20days, to minimize repetitive OPTIONS requests
# - various superluous settings to accommodate nonconformant browsers
#
# == Comment on echoing Access-Control-Allow-Origin
#
# How do you allow CORS requests only from certain domains? The last
# published W3C candidate recommendation states that the
# Access-Control-Allow-Origin header can include a list of origins.
# (See: http://www.w3.org/TR/2013/CR-cors-20130129/#access-control-allow-origin-response-header )
# However, browsers do not support this well and it likely will be
# dropped from the spec (see, http://www.rfc-editor.org/errata_search.php?rfc=6454&eid=3249 ).
#
# The usual workaround is for the server to keep a whitelist of
# acceptable origins (as a regular expression), match the request's
# Origin header against the list, and echo back the matched value.
#
# (Yes you can use '*' to accept all origins but this is too open and
# prevents using 'Access-Control-Allow-Credentials: true', which is
# needed for HTTP Basic Access authentication.)
#
# == Comment on spec
#
# Comments below are all based on my reading of the CORS spec as of
# 2013-Jan-29 ( http://www.w3.org/TR/2013/CR-cors-20130129/ ), the
# XMLHttpRequest spec (
# http://www.w3.org/TR/2012/WD-XMLHttpRequest-20121206/ ), and
# experimentation with latest versions of Firefox, Chrome, Safari at
# that point in time.
#
# == Changelog
#
# shared at: https://gist.github.com/algal/5480916
# based on: https://gist.github.com/alexjs/4165271
#
location / {
# if the request included an Origin: header with an origin on the whitelist,
# then it is some kind of CORS request.
# specifically, this example allow CORS requests from
# scheme : http or https
# authority : any authority ending in ".mckinsey.com"
# port : nothing, or :
if ($http_origin ~* (https?://[^/]*\.mckinsey\.com(:[0-9]+)?)$) {
set $cors "true";
}
# Nginx doesn't support nested If statements, so we use string
# concatenation to create a flag for compound conditions
# OPTIONS indicates a CORS pre-flight request
if ($request_method = 'OPTIONS') {
set $cors "${cors}options";
}
# non-OPTIONS indicates a normal CORS request
if ($request_method = 'GET') {
set $cors "${cors}get";
}
if ($request_method = 'POST') {
set $cors "${cors}post";
}
# if it's a GET or POST, set the standard CORS responses header
if ($cors = "trueget") {
# Tells the browser this origin may make cross-origin requests
# (Here, we echo the requesting origin, which matched the whitelist.)
add_header 'Access-Control-Allow-Origin' "$http_origin";
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true.
add_header 'Access-Control-Allow-Credentials' 'true';
# # Tell the browser which response headers the JS can see, besides the "simple response headers"
# add_header 'Access-Control-Expose-Headers' 'myresponseheader';
}
if ($cors = "truepost") {
# Tells the browser this origin may make cross-origin requests
# (Here, we echo the requesting origin, which matched the whitelist.)
add_header 'Access-Control-Allow-Origin' "$http_origin";
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true.
add_header 'Access-Control-Allow-Credentials' 'true';
# # Tell the browser which response headers the JS can see, besides the "simple response headers"
# add_header 'Access-Control-Expose-Headers' 'myresponseheader';
}
# if it's OPTIONS, then it's a CORS preflight request so respond immediately with no response body
if ($cors = "trueoptions") {
# Tells the browser this origin may make cross-origin requests
# (Here, we echo the requesting origin, which matched the whitelist.)
add_header 'Access-Control-Allow-Origin' "$http_origin";
# in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies)
add_header 'Access-Control-Allow-Credentials' 'true';
#
# Return special preflight info
#
# Tell browser to cache this pre-flight info for 20 days
add_header 'Access-Control-Max-Age' 1728000;
# Tell browser we respond to GET,POST,OPTIONS in normal CORS requests.
#
# Not officially needed but still included to help non-conforming browsers.
#
# OPTIONS should not be needed here, since the field is used
# to indicate methods allowed for "actual request" not the
# preflight request.
#
# GET,POST also should not be needed, since the "simple
# methods" GET,POST,HEAD are included by default.
#
# We should only need this header for non-simple requests
# methods (e.g., DELETE), or custom request methods (e.g., XMODIFY)
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# Tell browser we accept these headers in the actual request
#
# A dynamic, wide-open config would just echo back all the headers
# listed in the preflight request's
# Access-Control-Request-Headers.
#
# A dynamic, restrictive config, would just echo back the
# subset of Access-Control-Request-Headers headers which are
# allowed for this resource.
#
# This static, fairly open config just returns a hardcoded set of
# headers that covers many cases, including some headers that
# are officially unnecessary but actually needed to support
# non-conforming browsers
#
# Comment on some particular headers below:
#
# Authorization -- practically and officially needed to support
# requests using HTTP Basic Access authentication. Browser JS
# can use HTTP BA authentication with an XmlHttpRequest object
# req by calling
#
# req.withCredentials=true, and
# req.setRequestHeader('Authorization','Basic ' + window.btoa(theusername + ':' + thepassword))
#
# Counterintuitively, the username and password fields on
# XmlHttpRequest#open cannot be used to set the authorization
# field automatically for CORS requests.
#
# Content-Type -- this is a "simple header" only when it's
# value is either application/x-www-form-urlencoded,
# multipart/form-data, or text/plain; and in that case it does
# not officially need to be included. But, if your browser
# code sets the content type as application/json, for example,
# then that makes the header non-simple, and then your server
# must declare that it allows the Content-Type header.
#
# Accept,Accept-Language,Content-Language -- these are the
# "simple headers" and they are officially never
# required. Practically, possibly required.
#
# Origin -- logically, should not need to be explicitly
# required, since it's implicitly required by all of
# CORS. officially, it is unclear if it is required or
# forbidden! practically, probably required by existing
# browsers (Gecko does not request it but WebKit does, so
# WebKit might choke if it's not returned back).
#
# User-Agent,DNT -- officially, should not be required, as
# they cannot be set as "author request headers". practically,
# may be required.
#
# My Comment:
#
# The specs are contradictory, or else just confusing to me,
# in how they describe certain headers as required by CORS but
# forbidden by XmlHttpRequest. The CORS spec says the browser
# is supposed to set Access-Control-Request-Headers to include
# only "author request headers" (section 7.1.5). And then the
# server is supposed to use Access-Control-Allow-Headers to
# echo back the subset of those which is allowed, telling the
# browser that it should not continue and perform the actual
# request if it includes additional headers (section 7.1.5,
# step 8). So this implies the browser client code must take
# care to include all necessary headers as author request
# headers.
#
# However, the spec for XmlHttpRequest#setRequestHeader
# (section 4.6.2) provides a long list of headers which the
# the browser client code is forbidden to set, including for
# instance Origin, DNT (do not track), User-Agent, etc.. This
# is understandable: these are all headers that we want the
# browser itself to control, so that malicious browser client
# code cannot spoof them and for instance pretend to be from a
# different origin, etc..
#
# But if XmlHttpRequest forbids the browser client code from
# setting these (as per the XmlHttpRequest spec), then they
# are not author request headers. And if they are not author
# request headers, then the browser should not include them in
# the preflight request's Access-Control-Request-Headers. And
# if they are not included in Access-Control-Request-Headers,
# then they should not be echoed by
# Access-Control-Allow-Headers. And if they are not echoed by
# Access-Control-Allow-Headers, then the browser should not
# continue and execute actual request. So this seems to imply
# that the CORS and XmlHttpRequest specs forbid certain
# widely-used fields in CORS requests, including the Origin
# field, which they also require for CORS requests.
#
# The bottom line: it seems there are headers needed for the
# web and CORS to work, which at the moment you should
# hard-code into Access-Control-Allow-Headers, although
# official specs imply this should not be necessary.
#
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
# build entire response to the preflight request
# no body in this response
add_header 'Content-Length' 0;
# (should not be necessary, but included for non-conforming browsers)
add_header 'Content-Type' 'text/plain charset=UTF-8';
# indicate successful return with no content
return 204;
}
# --PUT YOUR REGULAR NGINX CODE HERE--
}
服務器解析流程如下:
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_header Access-Control-Allow-Origin *;
}
var img = new Image,
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = "http://example.com/image"; // insert image url here
img.crossOrigin = "Anonymous";
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
// make sure the load event fires for cached images too
if ( img.complete || img.complete === undefined ) {
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
img.src = src;
}
若服務器爲Apache,則可以按照如下配置:
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-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";
上述配置完成後,重啓服務器,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://blog.csdn.net/oyzl68/article/details/18741057