/dir/inner/other.html
<span style="font-size: 14px;"></span> 成功 |
<span style="font-size: 14px;"></span> |
https://store.company.com/secure.html
<span style="font-size: 14px;"></span> https |
和 |
http
)<span style="font-size: 14px;"></span> |
<span style="font-size: 14px;"></span>http://store.company.com:81/dir/ | 不同連接埠( 81和80)
<span style="font-size: 14px;"></span><span style="font-size: 14px;"></span> http://news.company.com/dir/other.html<span style="font-size: 14px;"></span><span style="font-size: 14px;"></span>失敗 <span style="font-size: 14px;"></span>
| 不同域名(
news<span style="font-size: 14px;"></span>和
|
store<span style="font-size: 14px;"></span>
| )<span style="font-size: 14px;"></span> |
而如果非同源,則有三種行為會受到限制:
而如果非同源,則有三種行為會受到限制:而如果非同源,則有三種行為會受到限制:
而如果非同源,則有三種行為會受到限制:而如果非同源,則有三種行為會受到限制:
而如果非同源,則有三種行為會受到限制:而如果非同源,則有三種行為會受到限制:
Cookie、LocalStorage和IndexDB無法讀取
<span style="font-size: 14px;"></span>規避同源策略(跨域)
<span style="font-size: 14px;"></span>Cookie<span style="font-size: 14px;"></span>
document.domain網頁但是,兩個網頁一級網域相同,只是二級網域不同,瀏覽器允許透過
document.domain<span style="font-size: 14px;">共享Cookie</span>
/例如,假設文件中的一個腳本在
:/ /store.company.com/dir/page.html<span style="font-size: 14px;"></span>執行以下語句:<span style="font-size: 14px;"></span><span style="font-size: 14px;">document.domain = "company.com"<br></span>
此時,<span style="font-size: 14px;">http://news.company.com/dir/other.html</span>
和 http://store.company.com/dir/other.html<span style="font-size: 14px;"></span>
就可以透過
document.cookie<span style="font-size: 14px;"></span>
來設定或取得Cookie,也就是共用Cookie。 <span style="font-size: 14px;"></span>
但是這種方法適用於Cookie和iframe窗口,LocalStorage和IndexDB無法透過這種方法來規避同源策略。 <span style="font-size: 14px;"></span>iframe<span style="font-size: 14px;"></span>
如果兩個網頁不同來源,就無法拿到對方的DOM,典型的例子是<span style="font-size: 14px;">iframe</span>
. ,如果和父視窗不同來源,則會報錯。 <span style="font-size: 14px;"></span>
此時如果兩個視窗一級網域相同,只是二級網域不同,那麼設定<span style="font-size: 14px;"></span>document.domain<span style="font-size: 14px;"></span>
屬性,就可以規避同源策略。 <span style="font-size: 14px;"></span>
而對於完全不同來源的網站,目前有三種方法可以解決跨域視窗之間的通訊問題。 <span style="font-size: 14px;"></span>
片段識別符(fragment identifier)<span style="font-size: 14px;"></span>
window.name<span style="font-size: 14px;"></span>
片段標識符 片段標識符(fragment identifier)指的是URL的#後面的部分,即
http://store.company.com/dir/other.html#fragment
<span style="font-size: 14px;"></span>的
#rag#rag (location.hash),如果只改變片段標識符,頁面不會重新刷新。 <span style="font-size: 14px;"></span>
父視窗可以把訊息寫入子視窗的片段標識符,子視窗透過監聽<span style="font-size: 14px;"></span>hashchange<span style="font-size: 14px;"></span>
事件得到通知。 <span style="font-size: 14px;"></span>
window.name<span style="font-size: 14px;"></span><span style="font-size: 14px;">每個iframe都有包裹它的window,這個window是top window的子窗戶,所以自然有</span>
window.name
<span style="font-size: 14px;"></span>window.name
名字,這個屬性的最大特點是,無論是否同源,只要在同一個視窗裡,視窗內所有頁面對window.name都有讀寫的權限。 <span style="font-size: 14px;"></span><span style="font-size: 14px;">window.name的值只能是字串的形式,這個字串的最大能允許2M左右甚至更大的一個容量,取決於不同的瀏覽器。 </span>
例如,想要在<span style="font-size: 14px;">http://example/a.html</span>
中获取<span style="font-size: 14px;">http://company.com/data.html</span>
中的数据,可以在a.html中使用一个隐藏的iframe,将iframe的src首先设置为<span style="font-size: 14px;">http://company.com/data.html</span>
,将其window.name设置为所需的数据内容,随后再将这个iframe的src设置为跟a.html页面同一个域的一个页面,不然a.html获取不到该iframe的window.name
<span style="font-size: 14px;">window.postMessage</span>
这是html5中新引入的一个API,可以使用它向其它的window对象发送消息,无论这个window对象属于同源还是不同源。
例如,父窗口<span style="font-size: 14px;">http://example/a.html</span>
向子窗口<span style="font-size: 14px;">http://company.com/data.html</span>
发送消息:
<span style="font-size: 14px;">var newWin = window.open('http://company.com/data.html', 'title')<br>newWin.postMessage('Hello World!'. 'http://company.com/data.html')<br></span>
<span style="font-size: 14px;">window.postMessage</span>
方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源,即<span style="font-size: 14px;">协议</span>
+<span style="font-size: 14px;">端口</span>
+<span style="font-size: 14px;">域名</span>
,也可以设置为<span style="font-size: 14px;">*</span>
,表示不限制域名。
子窗口向父窗口发送消息的写法类似:
<span style="font-size: 14px;">window.opener.postMessage('Nice to see you', 'http://example/a.html')<br></span>
子窗口和父窗口都可以通过<span style="font-size: 14px;">message</span>
时间,监听对方的消息。
<span style="font-size: 14px;">window.addEventListener('message', function(e) {<br> // ...<br>}, false)<br></span>
<span style="font-size: 14px;">message</span>
事件的事件对象<span style="font-size: 14px;">event</span>
有以下三个属性:
通过<span style="font-size: 14px;">window.postMessage</span>
,也可以读写其他窗口的<span style="font-size: 14px;">localStorage</span>
AJAX
同源策略规定,AJAX请求只能发给同源的网址,否则就报错,但是有三种方法可以规避这个限定:
JSONP
JSONP是服务器与客户端跨源通信的常用方法。基本思想是利用<span style="font-size: 14px;"><script></span>
请求脚本能够跨域访问的特性,先定义了一个回调方法,然后将其作为url参数的一部分发送到服务端,服务端通过字符串拼接的方式将数据包裹在回调方法中,再返回回来。
<span style="font-size: 14px;">// 网页动态插入`<script>`元素<br>function addScriptTag(src) {<br> var script = document.createElement("script")<br> script.setAttribute("type", "text/javascript")<br> srcipt.src = src<br> document.body.appendChild(script)<br>}<br><br>window.onload = function() {<br> addScriptTag('http://example.com/ip?callback=foo')<br>}<br><br>function foo(data) {<br> // ...<br>}<br></span>
WebSocket
WebSocket是一种通信协议,使用<span style="font-size: 14px;">ws://</span>
(非加密)和<span style="font-size: 14px;">wss://</span>
(加密)作为协议前缀。该协议不实行同源政策,只要服务支持,就可以通过它进行跨源通信。
浏览器发出的WebSocket请求的头信息中含有<span style="font-size: 14px;">Origin</span>
字段,表示该请求的请求源,即发自哪个域名。(加入白名单)
CORS
跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种使用额外的HTTP头来使一个用户代理从一个不同于当前站点(域)的服务器获取指定的资源的机制。用户代理使用跨域HTTP请求来获取与当前文档不同域、不用协议或端口的资源。
出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。而跨域资源共享(CORS)机制允许web应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在API容器中(例如<span style="font-size: 14px;">XMLHttpRequest</span>
或<span style="font-size: 14px;">Fetch</span>
)使用CORS,以降低跨域HTTP请求所带来的风险。
跨域资源共享标准允许在下列场景中使用跨域HTTP请求:
由XMLHttpRequest或Fetch发起的跨域HTTP请求;
web字体(CSS中通过<span style="font-size: 14px;">@font-face</span>
使用跨域字体资源),因此,网站就可以发布TrueType字体资源,并只允许已授权网站进行跨站调用;
WebGL贴图;
使用drawImage将Images/video画面绘制到canvas;
样式表(使用CSSOM);
Scripts(未处理的异常)。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
只要同时满足以下两大条件,就属于简单请求:
1 请求方法是以下三种方法之一:
<span style="font-size: 14px;">- HEAD<br>- GET<br>- POST<br></span>
2 HTTP的头信息不超出以下几种字段:
<span style="font-size: 14px;">- Accept<br>- Accept-Language<br>- Content-Language<br>- Last-Event-ID<br>- Content-Type:(只限三个值:application/x-www-form-urlencoded、multipart/from-data、text/plain)<br></span>
只要不同时满足上面两个条件,就属于非简单请求
简单请求
对于简单请求,浏览器会在请求头部增加一个<span style="font-size: 14px;">Origin</span>
字段。这个字段用来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值决定是否同意这次请求。
如果<span style="font-size: 14px;">Origin</span>
指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。而这个回应的头信息不包含<span style="font-size: 14px;">Access-Control-Allow-Origin</span>
字段,从而会抛出错误被<span style="font-size: 14px;">XMLHttpRequest</span>
的<span style="font-size: 14px;">onerror</span>
函数捕获,(回应的状态码有可能是200)。
如果<span style="font-size: 14px;">Origin</span>
指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段
<span style="font-size: 14px;">Access-Control-Allow-Origin: ...<br>Access-Control-Allow-Credentials: true<br>Access-Control-Expose-Headers: callback<br>Content-Type: text/html; charset=utf-8<br></span>
<span style="font-size: 14px;">Access-Control-Allow-Origin</span>
: 必须。值要么是请求时<span style="font-size: 14px;">Origin</span>
的值,要么是'*'
<span style="font-size: 14px;">Access-Control-Allow-Credentials</span>
: 可选。布尔值,决定是否允许发送Cookie,不需要则删除该字段。
<span style="font-size: 14px;">Access-Control-Expose-Headers</span>
: 可选。CORS请求时,<span style="font-size: 14px;">XMLHttpRequest</span>
对象的 <span style="font-size: 14px;">getResponseHeader()</span>
方法只能拿到6个基本字段:<span style="font-size: 14px;">Cache-Control</span>
、<span style="font-size: 14px;">Content-Language</span>
、<span style="font-size: 14px;">Content-Type</span>
、<span style="font-size: 14px;">Expires</span>
、<span style="font-size: 14px;">Last-Modified</span>
、<span style="font-size: 14px;">Pragma</span>
。如果想拿到其他字段,就需要在<span style="font-size: 14px;">Access-Control-Expose-Header</span>
里面指定。上面的例子指定为callback,则可以使用<span style="font-size: 14px;">getResponseHeader(callback)</span>
获取callback字段的值。
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发送到服务器,一方面要服务器同意,指定<span style="font-size: 14px;">Access-Control-Allow-Credentials</span>
字段,另一方面,开发者需要在AJAX请求中设置<span style="font-size: 14px;">withCredentials</span>
属性:
<span style="font-size: 14px;">var xhr = new XMLHttpRequest()<br>xhr.withCredentials = true<br></span>
否则,即使服务器同意发送Cookie,浏览器也不会发送。
需要注意的是,如果要发送Cookie,<span style="font-size: 14px;">Access-Control-Allow-Origin</span>
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源策略,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且跨域的原网页中的<span style="font-size: 14px;">document.cookie</span>
操作也无法获取嗷服务器域名下的Cookie。
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是<span style="font-size: 14px;">PUT</span>
或<span style="font-size: 14px;">DELETE</span>
,或者<span style="font-size: 14px;">Content-Type</span>
字段的类型是<span style="font-size: 14px;">application/json</span>
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP操作和头信息字段。只有得到肯定答复,浏览器才会发出正式的<span style="font-size: 14px;">XMLHttpRequest</span>
请求,否则就报错。
“预检”请求用请求方法是<span style="font-size: 14px;">OPTIONS</span>
,表示这个请求是用来询问的。头信息里面,关键字段是<span style="font-size: 14px;">Origin</span>
,表示请求来自哪个源。
还有以下两个特殊字段:
服务器返回的响应:
<span style="font-size: 14px;">Access-Control-Allow-Methods: ...<br>Access-Control-Expose-Headers: callback<br>Access-Control-Allow-Credentials: true<br>Access-Control-Max-Age: 1728000<br></span>
Access-Control-Allow-Methods: 必须。逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。为了避免多次“预检”行为。
Access-Control-Expose-Headers: 如果瀏覽器請求包含<span style="font-size: 14px;">Access-Control-Request-Headers</span>
字段,則<span style="font-size: 14px;">. 。它也是一個逗號分隔的字串,表示伺服器支援的所有頭資訊字段,不限於瀏覽器在"預檢"中請求的字段。 </span>
<span style="font-size: 14px;"></span>
Access-Control-Allow-Credentials: 與簡單請求時的含義相同。 <span style="font-size: 14px;"></span>
Access-Control-Max-Age: 本次預檢請求的有效期限。 <span style="font-size: 14px;"></span>
CORS與JSONP的比較
<span style="font-size: 14px;"></span>CORS與JSONP的使用目的相同,但是比JSONP更強大。
<span style="font-size: 14px;"></span>JSONP只支援
GET<span style="font-size: 14px;">請求,CORS支援所有類型的HTTP請求。 JSONP的優勢在於支援老式瀏覽器,以及可以向不支援CORS的網站請求資料。 </span>
<span style="font-size: 14px;"></span>相關建議:
<span style="font-size: 14px;"></span>
用Js實現懶載與跨域的實作步驟