Django 學習之CORS


跨域

跨域指的就是“跨域資源共享(Cross-Origin Resource Sharing, CORS)”,是一個“W3C標准”,當一個資源從與該資源本身所在的服務器的不同域或者不同端口請求一個資源時,就會發起一個跨域HTTP請求。

說到跨域,肯定就要講一下 同源策略(Same origin policy),該策略是由Netscape(網景)公司在1995年引入瀏覽器的一個著名的安全策略,是一種約定,也是瀏覽器最核心,最基本的安全功能,如果少了同源策略,瀏覽器的正常功能可能都會收到影響,可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。目前所有支持JavaScript的瀏覽器都會使用這個策略。

 

同源的目的

為了保證用戶信息的安全,防止惡意網站竊取數據
設想這樣一種情況:A網站是一家銀行,用戶登錄后,產生了Cookie,在沒有同源策略的情況下,又去瀏覽其他網站,則其他網站可以讀取到A網站的Cookie,會發生什么?很顯然,如果Cookie 包含隱私(比如存款總額)這些信息就會泄露,更可怕的是,Cookie往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。
所以同時打開的B網頁,不能訪問A頁的Cookie,除非這兩個網頁“同源”,如果非同源,那么請求數據的時候,瀏覽器會在控制台中報一個異常,提示拒絕訪問。

什么是同源

協議相同:比如http://
域名相同:比如“www.test.com”
端口相同:比如同為8000(默認端口80可以省略)
同時滿足滿足以上三個條件的才可以被稱為同源

舉例說明:http://www.example.com/dir/page.html 這個網址,協議是http://, 域名是www.example.com,端口是80(默認端口可以省略),它的同源情況如下:

A.http://www.example.com/dir2/other.html 同源
B.http://www.example.cn/dir/other.html 不同源(域名不同)
C.http://example.com/dir/other.html 不同源(域名不同)
D.http://v2.www.example.com/dir/other.html 不同源(域名不同)
E.http://www.example.com:81/dir/other.html 不同源(端口不同)

 

非同源的限制

1.Cookie、LocalStorage、IndexDB 無法讀取
2.DOM 無法獲得
3.AJAX 請求不能發送
由於本文主要是介紹Django目前最優的解決跨域請求的方法,對避免上述三種限制有興趣的小伙伴可以去阮老師的《瀏覽器同源策略及其規避方法》的http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

 

回歸Django的跨域的解決方案

參考資料:https://github.com/ottoyiu/django-cors-headers/
原作者測試的組合:
Python:2.7、 3.6
Django:1.8、1.9、1.10、1.11、2.0、2.1
我所使用的組合:
Python:3.6 Django:1.11.11
具體步驟:

1.pip 安裝:
在所使用的虛擬環境中安裝:
pip install django-cors-headers

2.將其注冊到Django 項目setting的INSTALLED_APPS中:
INSTALLED_APPS = (
...,
'corsheaders',
...

3.添加中間件類來監聽響應
# ‘CorsMiddleware’ 圖省事可以放在最外層
推薦盡可能放置在生成響應的任何中間件之前,
如Django 'CommonMiddleware' ,如果不是,則會跨域失敗

MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]

4.需要在Django中使用的配置

CORS_ORIGIN_WHITELIST
允許白名單,允許執行跨站請求的主機列表(疑問添加'null',待解決),默認是:[ ]
如:
CORS_ORIGIN_WHITELIST = (
'12.0.0.1:8000',
'google.com',
'localhost:8000',
)

CORS_ALLOW_CREDENTIALS
跨域請求時是否允許攜帶Cookie,默認是False
CORS_ALLOW_CREDENTIALS = True

# 實現以上配置功能即可進行跨域

 


# 配置擴展(以下配置,根據需求進行添加)

CORS_ORIGIN_ALLOW_ALL
如果是True,將不再使用白名單,並允許所有主機執行跨站點請求,默認是False

CORS_ORIGIN_REGEX_WHITELIST
當你有大量子域名需要添加到白名單的時候,‘CORS_ORIGIN_WHITELIST’ 就不太適用了,可以通過此設置將對HTTP請求進行正則匹配,匹配成功則允許跨域,否則不允許,默認是[ ]
如:
CORS_ORIGIN_REGEX_WHITELIST = (
r'^(https?://)?(\w+\.)?google\.com$',
r'^(https?://)?(\w+\.)?baidu\.com$',
)

CORS_URLS_REGEX
一個正則表達式,限制所有進行CORS 的URL,默認是 r'^.*' 即匹配所有URL,當您只需要一部分請求CORS時,很有用,例如API /api/:
CORS_URLS_REGEX = r"^/api/.*$"

CORS_ALLOW_METHODS
允許跨域的請求方式,一般使用默認,如下:
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)
擴展添加額外的請求方式時可以導入默認值,如:
from corsheaders.defaults import default_methods
CORS_ALLOW_METHODS = default_methods + (
'POKE' # 自定義請求方式
)

CORS_ALLOW_HEADERS
允許跨域的請求頭,一般使用默認值,如:
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)
同上一樣,可以導入默認值,來實現自定義請求頭的擴展
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = default_headers + (
'my-custom-header',
)

CORS_EXPOSE_HEADERS
向瀏覽器公開的HTTP請求頭列表,默認是 [ ]

CORS_MODEL
幾乎用不到,留坑,以后擴展。默認為None

CORS_PREFLIGHT_MAX_AGE
瀏覽器或者客戶端可以緩存預檢響應的秒數,如果是0或者任何假值,則再次發送請求時,還需要進行預檢,默認是86400(一天)
擴展:預檢是發送“非簡單請求”時,發送真正請求前的額外請求


簡單請求與非簡單請求

同時滿足以下兩大條件,就屬於簡單請求,否則就是復雜請求
一、請求方式
1.HEAD
2.GET
3.POST
二、請求頭信息
1.Accept
2.Accept-Language
3.Content-Language
4.Last-Event-ID
5.Content-Type 只限於三個值:application/x-www-form-urlencoded、multipart/form-data、text/plain

針對簡單請求和復雜請求,瀏覽器的處理方式

簡單請求
瀏覽器直接發出CORS 請求 ,具體來說就是在請求頭信息中添加一個"Origin"的字段
Origin字段的作用,就是說明本次請求來自哪個源(協議+域名+端口),服務器根據這個值,決定是否同意這次請求
1.如果Origin指定的源不在許可范圍內,服務器會返回一個頭信息里面不包含“Access-Control-Allow-Origin”字段的正常響應,瀏覽器發現沒有該字段后,就會拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲。注意,因為這種HTTP回應的狀態碼可能是200,所以無法通過狀態碼識別或進行條件判斷。
2.如果Origin指定的源在許可范圍內,服務器的返回的響應里面,會多出四個頭信息字段,其中三個字段與CORS請求相關,且都是以“Access-Control-” 開頭

Access-Control-Allow-Origin
該字段是必須的,它的值要么是請求時Origin字段的值,要么時一個“*”,表示接收任意域名的請求

Access-Control-Allow-Credentials
該字段可選,值是一個布爾值,表示是否允許發送Cookie,默認為False
如果需要將Cookie 包含在請求頭中,一起發給服務器,則必須將此字段設為True

Access-Control-Expose-Headers
該字段可選,CORS請求時,XMLHttpRequest 對象的getResponseHeader()方法只能拿到6個基本字段“Cache-Control(緩存控制)”,“Content-Language(指明報文體使用的語言,譬如:ch,fr,en,ja等等)”,“Content-Type(指定報文體的類型,比如text/xml,image/jpeg等等,同時可以通過charset來指定內容所使用的字符集)”,“Expires(用來控制緩存的失效期,如果Cache-Control設置“max-age”或"s-max-age",則會忽略該字段)”,“Last-Modified(請求成功后用來標記此文件在服務器端最后被修改的時間,如果第二次進行請求發現服務器資源沒有發生變化,此字段值不變,則自動重定向並返回304(Not Changed)的狀態碼)”,“Pragma(HTTP/1.0時功能比較弱的緩存機制,HTTP/1.0存在該字段時會忽略“Expires”和“Cache-control”字段)”,如果希望該方法能拿到其他字段,就必須要在‘Access-Control-Expose-Headers’中指定

非簡單請求
對服務器又特殊要求的請求,常見的如請求方式時“PUT”或"DELETE",或者請求頭Content-Type字段的類型是application/json時
非簡單請求的CORS請求,會在正式通信前,增加一次HTTP查詢請求,稱為“預檢(preflight)”請求
預檢其實就是做檢查,檢查如果通過則允許傳輸數據,檢查不通過則不能發送真正要發送的消息
預檢的請求方式:OPTIONS, 表示這個請求時用來詢問的
預檢的請求頭信息的關鍵字段

Origin
表示請求來自哪個源

Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些請求方式,會在“CORS_ALLOW_METHODS”中查找,存在則通過,不存在則不允許跨域,或在“CORS_ALLOW_METHODS”中添加該請求方式

Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,會在“CORS_ALLOW_HEADERS”中查找,存在則通過,不存在則不允許跨域,或在“CORS_ALLOW_HEADERS中”添加該頭信息


另外一種跨域的解決方案

JSOBPJSONP
只能發送GET請求,主要修改在前段部分,后端需要做約束修改,發jsonp請求。優勢在於支持老式瀏覽器,以及向不支持CORS的網站請求數據。

 

相關資料參考:

Django-cors-headers:https://github.com/ottoyiu/django-cors-headers/

瀏覽器同源策略及其規避方法:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

Django跨域請求:https://www.cnblogs.com/Joe1991/articles/8483770.html

跨域資源共享CORS 詳解: http://www.ruanyifeng.com/blog/2016/04/cors.html

 


免責聲明!

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



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