在Web開發(fā)中衬衬,經常會碰到跨站請求夜矗。比如A域名下的站點http://domainA通過AJAX請求B域名下的站點http://domainB的資源(比如圖片妆偏,javascript腳本或其它類資源)我磁。
出于安全考慮圃阳,瀏覽器會限制腳本中發(fā)起的跨站請求伞租。比如,使用XMLHttpRequest對象發(fā)起的HTTP請求就必須遵循同源策略限佩。即Web應用程序只能使用XMLHttpRequest對象向其加載的源域名發(fā)起HTTP請求葵诈,而不能向任何其它域名發(fā)起請求。
跨源資源共享(Cross-Orgin Resource Sharing(CORS))機制讓Web應用服務器能支持跨站訪問祟同,從而使得安全的進行跨站數(shù)據(jù)傳輸成為可能作喘。
CORS在以下場景可以使用跨站HTTP請求:
1,XMLHttpRequest發(fā)起的跨站HTTP請求
2晕城,Web字體
3泞坦,WebGL貼圖
4,使用drawImage API在canvas上畫圖
簡單請求
所謂的簡單砖顷,是指:
1贰锁,只使用 GET, HEAD 或者 POST 請求方法。如果使用 POST 向服務器端傳送數(shù)據(jù)滤蝠,則數(shù)據(jù)類型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一種豌熄。
2,不會使用自定義請求頭(類似于 X-Modified 這種)物咳。
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
預請求
不同于上面討論的簡單請求锣险,“預請求”要求必須先發(fā)送一個 OPTIONS 請求給目的站點,來查明這個跨站請求對于目的站點是不是安全可接受的览闰。這樣做芯肤,是因為跨站請求可能會對目的站點的數(shù)據(jù)造成破壞。 當請求具備以下條件压鉴,就會被當成預請求處理:
1, 請求以 GET, HEAD 或者 POST 以外的方法發(fā)起請求崖咨。或者油吭,使用 POST击蹲,但請求數(shù)據(jù)為 application/x-www-form-urlencoded, multipart/form-data 或者 text/plain 以外的數(shù)據(jù)類型。比如說上鞠,用 POST 發(fā)送數(shù)據(jù)類型為 application/xml 或者 text/xml 的 XML 數(shù)據(jù)的請求际邻。
2, 使用自定義請求頭(比如添加諸如 X-PINGOTHER)
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}Arun';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
......
服務器與瀏覽器之間具體的交互過程:
使用一個 OPTIONS 發(fā)送了一個“預請求”芯丧,來探明服務器端是否接受后續(xù)真正的請求芍阎。
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache
Arun
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some GZIP'd payload]
隨同 OPTIONS 請求,以下兩個請求頭一起被發(fā)送:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER
請求頭Access-Control-Request-Method可以提醒服務器跨站請求將使用POST方法缨恒,而請求頭Access-Control-Request-Headers則告知服務器該跨站請求將攜帶一個自定義請求頭X-PINGOTHER谴咸。這樣轮听,服務器就可以決定,在當前情況下岭佳,是否接受該跨站請求訪問血巍。
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
響應頭Access-Control-Allow-Methods表明服務器可以接受POST, GET和 OPTIONS的請求方法。響應頭Access-Control-Allow-Headers則表示服務器接受自定義請求頭X-PINGOTHER珊随。最后述寡,響應頭Access-Control-Max-Age告訴瀏覽器,本次“預請求”的響應結果有效時間是多久叶洞。在上面的例子里鲫凶,1728000秒代表著20天內,瀏覽器在處理針對該服務器的跨站請求衩辟,都可以無需再發(fā)送“預請求”螟炫,只需根據(jù)本次結果進行判斷處理。