2020-03-12:
讀后筆記,原文跳轉:
1、瀏覽器同源政策及其規避方法,阮一峰,link:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
2、跨域資源共享 CORS 詳解,阮一峰,link:https://www.ruanyifeng.com/blog/2016/04/cors.html
3、aiohttp_cors 庫,為aiohttp異步HTTP服務器實現了 跨源資源共享(CORS)支持,link:https://github.com/aio-libs/aiohttp-cors#usage
cookie:Cookie 包含隱私(比如存款總額)。Cookie 往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶。(后面出一個blog詳細學習cookie)
一、什么是瀏覽器同源政策:
1、同源:協議(http)、域名(www.xxx.com)、端口(8080)都相同,稱其為同源。
2、目的:保護用戶信息安全,a網站設置的cookie只能用於同源網站。"同源政策"是必需的,否則 Cookie 可以共享,互聯網就毫無安全可言了。
3、同源帶來的限制:
a、Cookie、LocalStorage 和 IndexDB 無法讀取;
b、DOM 無法獲得;
c、AJAX 請求不能發送。(重點來了,這個導致了CORS的出現)
4、如何規避上述限制:
(1)、一級域名相同二級域名不同時如何共享cookie? : 瀏覽器設置 document.domain='xxx.com'; 以
共享 Cookie。另外,服務器端設置:Set-Cookie: doman='.xxx.com'; path=/
(2)iframe
窗口和window.open
方法打開的窗口,它們與父窗口無法通信,原因是兩個網頁不同源無法拿到對方的DOM,如何解決? :
a、一級域名相同,二級域名不同,方法同上,設置document.domain
b、兩個網站完全不同源:(iframe跨域問題)
i、片段識別符(fragment identifier):URL的#號后面的部分。父窗口可以把信息寫入子窗口的片段識別符,子窗口通過監聽hashchange事件得到通知。反之,子窗口也可以改變父窗口的片段識別符。
ii、瀏覽器窗口的window.name 屬性:無論是否同源,只要在同一個窗口里,前一個網頁設置了這個屬性,后一個網頁可以讀取它。優點是,window.name
容量很大,可以放置非常長的字符串;缺點是必須監聽子窗口window.name
屬性的變化,影響網頁性能。
iii、html5新api,跨文檔通信API,新增 window.postMessage 方法,父窗口和子窗口都可以通過message
事件,監聽對方的消息。
(3)localStorage 的跨域解決:利用window.postMessage 方法,把localStorage將通過消息發送進行通訊。
(4)ajax跨域請求:
a、服務器代理:瀏覽器請求同源服務器,再由后者請求外部服務;
b、JSONP:網頁通過添加一個<script>
元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。缺點:只能發GET請求。
c、websocket : WebSocket是一種通信協議,使用ws://
(非加密)和wss://
(加密)作為協議前綴。該協議不實行同源政策,只要服務器支持,就可以通過它進行跨源通信。其中,有一個字段是Origin
,表示該請求的請求源(origin),即發自哪個域名。
d、CORS:下文詳述。
二、什么是跨源資源分享(CORS):
1、條件:需要瀏覽器、服務器同時支持,瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。
2、分類:簡單請求和非簡單請求。區分點,滿足的就是簡單請求:
3、簡單請求:
對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin
字段。Origin
字段用來說明,本次請求來自哪個源(協議 + 域名 + 端口)。
怎么知道請求是否通過?-- 無論如何,瀏覽器都會返回200,所以狀態碼不可靠。需要通過回應的頭信息沒有包含Access-Control-Allow-Origin 判斷是否成功,這部分瀏覽器會自動檢查和發起錯誤。
-- Origin
指定的域名在許可范圍內,服務器返回的響應,會多出幾個頭信息字段:
(1)Access-Control-Allow-Origin;該字段是必須的。它的值要么是請求時Origin
字段的值,要么是一個*
,表示接受任意域名的請求。
(2)Access-Control-Allow-Credentials; (可選,服務器端)表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true
,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。另一方面,必須在AJAX請求中打開withCredentials
屬性。如果要發送Cookie,Access-Control-Allow-Origin
就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁代碼中的document.cookie
也無法讀取服務器域名下的Cookie。
(3)Access-Control-Expose-Headers; (可選)CORS請求時,XMLHttpRequest
對象的getResponseHeader()
方法只能拿到6個基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必須在Access-Control-Expose-Headers
里面指定。
4、非簡單請求:
(略)
5、CORS與JSONP的比較:
JSONP只支持GET
請求,CORS支持所有類型的HTTP請求。JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。
三、aiohttp-cors 的應用:
要使用aiohttp_cors,您需要配置應用並在要公開的資源和路由上啟用CORS:
# 1、基本用法: from aiohttp import web import aiohttp_cors app = web.Application() cors = aiohttp_cors.setup(app) # 返回`aiohttp_cors.CorsConfig`實例 # 要為特定路線使用CORS,您需要添加路由到CORS配置對象並指定其CORS選項。 resource = cors.add(app.router.add_resource("/hello")) route = cors.add( resource.add_route("GET", handler), { "http://client.example.org": aiohttp_cors.ResourceOptions( allow_credentials=True, # 是否允許證書 expose_headers=("X-Custom-Server-Header",), # 該資源將暴露X-Custom-Server-Header 給客戶端。 allow_headers=("X-Requested-With", "Content-Type"), # 客戶端被允許把這些header傳給服務端 max_age=3600, # 客戶端可以將預檢請求緩存3600秒 ) })
# 2、資源可以針對不同源可以有不同的規則: cors.add(route, { "*": aiohttp_cors.ResourceOptions(allow_credentials=False), "http://client.example.org": aiohttp_cors.ResourceOptions(allow_credentials=True), }) # “*”(除xxx之外所有其他源):允許向其提供內容,但不允許向其提供證書; # with allowed credentials passing only to http://client.example.org.
# 3、指定默認的啟用CORS的資源選項,然后再補充該資源只能被請求的源和請求的方式: cors = aiohttp_cors.setup(app, defaults={ # Allow all to read all CORS-enabled resources from # http://client.example.org. "http://client.example.org": aiohttp_cors.ResourceOptions(), }) hello_resource = cors.add(app.router.add_resource("/hello")) cors.add(hello_resource.add_route("POST", handler_post)) cors.add(hello_resource.add_route("PUT", handler_put)) # 也能這么寫: hello_resource = cors.add(app.router.add_resource("/hello"), { "http://client.example.org": aiohttp_cors.ResourceOptions(), }) cors.add(hello_resource.add_route("POST", handler_post)) cors.add(hello_resource.add_route("PUT", handler_put)) # 還能這么寫: hello_resource = cors.add(app.router.add_resource("/hello"), { "http://client.example.org": aiohttp_cors.ResourceOptions(allow_methods=["POST", "PUT"]), })
# 4、例子:允許所有源使用包含所有功能的CORS cors = aiohttp_cors.setup(app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*", ) }) # Add all resources to `CorsConfig`. resource = cors.add(app.router.add_resource("/hello")) cors.add(resource.add_route("GET", handler_get)) cors.add(resource.add_route("PUT", handler_put)) cors.add(resource.add_route("POST", handler_put)) cors.add(resource.add_route("DELETE", handler_delete))