筆記:同源政策、跨域資源共享CORS、aiohttp的CORS實現


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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必須在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))

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM