一、什么是跨域
跨域是指:瀏覽器A從服務器B獲取的靜態資源,包括Html、Css、Js,然后在Js中通過Ajax訪問C服務器的靜態資源或請求。即:瀏覽器A從B服務器拿的資源,資源中想訪問服務器C的資源。
同源策略是指:瀏覽器A從服務器B獲取的靜態資源,包括Html、Css、Js,為了用戶安全,瀏覽器加了限制,其中的Js通過Ajax只能訪問B服務器的靜態資源或請求。即:瀏覽器A從哪拿的資源,那資源中就只能訪問哪。
同源是指:同一個請求協議(如:Http或Https)、同一個Ip、同一個端口,3個全部相同,即為同源。
跨域的處理
W3C組織制定了一個Cross-Origin Resource Sharing規范,簡寫為Cors,現在這個規范已經被大多數瀏覽器支持,Cors 專門用來處理跨域的需求。
Cors需要在后端應用進行配置,因此,是一種跨域的后端處理方式,這么做也容易理解,一個你不認識的源來訪問你的應用,自然需要應用進行授權。除了后端處理方式,也有前端的解決方案,如:JSONP,這里我們主要講解Flask后端配置方案.
跨域的分類
跨域分為以下3種
名稱 | 英文名 | 說明 |
---|---|---|
簡單請求 | Simple Request | 發起的Http請求符合: 1.無自定義請求頭,只有Accept、Accept-Language、Content-Language、Last-Event-ID 2.請求動詞為GET、HEAD或POST之一 3.動詞為POST時,Content-Type是application/x-www-form-urlencoded,multipart/form-data或text/plain之一 |
復雜請求 | Preflighted Request | 發起的Http請求符合其中之一: 1.包含了自定義請求頭 2.請求動詞不是GET、HEAD或POST 3.動詞是POST時, Content-Type不是application/x-www-form-urlencoded,multipart/form-data或text/plain。 即:簡單請求的相反 |
憑證請求 | Requests with Credential | 發起的Http請求中帶有憑證 |
簡單請求
簡單請求的發送從代碼上來看和普通的XHR沒太大區別,但是HTTP頭當中要求總是包含一個域(Origin)的信息。該域包含協議名、地址以及一個可選的端口。不過這一項實際上由瀏覽器代為發送,并不是開發者代碼可以觸及到的。
簡單請求的部分響應頭如下:
- Access-Control-Allow-Origin(必含)。不填,請求按失敗處理。該項控制數據的可見范圍,如果希望數據對任何人都可見,可以填寫"*"。
- Access-Control-Allow-Credentials(可選)。該項標志著請求當中是否包含cookies信息,只有一個可選值:true(必為小寫)。如果不包含cookies,請略去該項,而不是填寫false。這一項與XmlHttpRequest2對象當中的withCredentials屬性應保持一致,即withCredentials為true時該項也為true;withCredentials為false時,省略該項不寫。反之則導致請求失敗。
- Access-Control-Expose-Headers(可選) – 該項確定XmlHttpRequest2對象當中getResponseHeader()方法所能獲得的額外信息。通常情況下,getResponseHeader()方法只能獲得如下的信息:(當你需要訪問額外的信息時,就需要在這一項當中填寫并以逗號進行分隔)
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
如果僅僅是簡單請求,那么即便不用CORS也沒有什么大不了,但CORS的復雜請求就令CORS顯得更加有用了。簡單來說,任何不滿足上述簡單請求要求的請求,都屬于復雜請求。比如說你需要發送PUT、DELETE等HTTP動作,或者發送Content-Type: application/json的內容。
復雜請求
復雜請求先發送一種"預請求",此時作為服務端,也需要返回"預回應"作為響應。預請求實際上是對服務端的一種權限請求,只有當預請求成功返回,實際請求才開始執行。預請求以OPTIONS形式發送,當中同樣包含域,并且還包含了兩項CORS特有的內容:
- Access-Control-Request-Method?– 該項內容是實際請求的種類,可以是GET、POST之類的簡單請求,也可以是PUT、DELETE等等。
- Access-Control-Request-Headers?– 該項是一個以逗號分隔的列表,當中是復雜請求所使用的頭部。
顯而易見,這個預請求實際上就是在為之后的實際請求發送一個權限請求,在預回應返回的內容當中,服務端應當對這兩項進行回復,以讓瀏覽器確定請求是否能夠成功完成。復雜請求的部分響應頭及解釋如下:
- Access-Control-Allow-Origin(必含) – 和簡單請求一樣的,必須包含一個域。
- Access-Control-Allow-Methods(必含) – 這是對預請求當中Access-Control-Request-Method的回復,這一回復將是一個以逗號分隔的列表。盡管客戶端或許只請求某一方法,但服務端仍然可以返回所有允許的方法,以便客戶端將其緩存。
- Access-Control-Allow-Headers(當預請求中包含Access-Control-Request-Headers時必須包含) – 這是對預請求當中Access-Control-Request-Headers的回復,和上面一樣是以逗號分隔的列表,可以返回所有支持的頭部。這里在實際使用中有遇到,所有支持的頭部一時可能不能完全寫出來,而又不想在這一層做過多的判斷,沒關系,事實上通過request的header可以直接取到Access-Control-Request-Headers,直接把對應的value設置到Access-Control-Allow-Headers即可。
- Access-Control-Allow-Credentials(可選) – 和簡單請求當中作用相同。
- Access-Control-Max-Age(可選) – 以秒為單位的緩存時間。預請求的的發送并非免費午餐,允許時應當盡可能緩存。
一旦預回應如期而至,所請求的權限也都已滿足,則實際請求開始發送。
目前大部分Modern瀏覽器已經支持完整的CORS,但IE直到IE11才完美支持,所以對于PC網站,還是建議采用其他解決方案,如果僅僅是移動端網站,大可放心使用。
Flask配置跨域
flask 配置cors 有兩種方式,一種使用自帶的cors,一種自定義返回頭
第一種,自定義返回頭
@app.after_request
def af_request(resp): """#請求鉤子,在所有的請求發生后執行,加入headers。:param resp::return:"""resp = make_response(resp)resp.headers['Access-Control-Allow-Origin'] = '*'resp.headers['Access-Control-Allow-Methods'] = 'GET,POST'resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'return resp
第二種,使用cors
Flask配Cors跨域,使用Flask-CORS包,詳細文檔,參見:?https://flask-cors.readthedocs.io/en/latest/
flask-cors 也提供了兩種方式:
方式 | 范圍 | 說明 |
---|---|---|
@cross_origin裝飾器 | 配置單個路由 | 適用于配置特定的API接口 |
CORS函數 | 配置全局API接口 | 適用于全局的API接口配置 |
安裝flask-cors
pip install flask-cors
第一種:使用@cross_origin裝飾器
@app.route("/")
@cross_origin()
def helloWorld():return "Hello, cross-origin-world!"
CORS參數說明?
裝飾器參數 | 類型 | Head字段 | 說明 |
---|---|---|---|
origins | 列表、字符串或正則表達式 | Access-Control-Allow-Origin | 配置允許跨域訪問的源, |
methods | 列表、字符串 | Access-Control-Allow-Methods | 配置跨域支持的請求方式,如:GET、POST |
expose_headers | 列表、字符串 | Access-Control-Expose-Headers | 自定義請求響應的Head信息 |
allow_headers | 列表、字符串或正則表達式 | Access-Control-Request-Headers | 配置允許跨域的請求頭 |
supports_credentials | 布爾值 | Access-Control-Allow-Credentials | 是否允許請求發送cookie,false是不允許 |
max_age | 整數、字符串 | Access-Control-Max-Age | 預檢請求的有效時長 |
第二種:使用CORS函數
#################### 應用全局配置 ####################
from flask_cors import *app = Flask(__name__)
CORS(app, supports_credentials=True, resources=r"/*")@app.route("/api/v1/users")
def list_users():return "user example"#################### 單獨Blueprints配置 ####################
api_v1 = Blueprint('API_v1', __name__)
CORS(api_v1) @api_v1.route("/api/v1/users/")
def list_users():return "user example"
CORS參數說明
參數 | 類型 | Head字段 | 說明 |
---|---|---|---|
resources | 字典、迭代器或字符串 | 無 | 全局配置允許跨域的API接口 |
origins | 列表、字符串或正則表達式 | Access-Control-Allow-Origin | 配置允許跨域訪問的源, |
methods | 列表、字符串 | Access-Control-Allow-Methods | 配置跨域支持的請求方式,如:GET、POST |
expose_headers | 列表、字符串 | Access-Control-Expose-Headers | 自定義請求響應的Head信息 |
allow_headers | 列表、字符串或正則表達式 | Access-Control-Request-Headers | 配置允許跨域的請求頭 |
supports_credentials | 布爾值 | Access-Control-Allow-Credentials | 是否允許請求發送cookie,false是不允許 |
max_age | 整數、字符串 | Access-Control-Max-Age | 預檢請求的有效時長 |
注意:繼承Resource類來寫的RESTful-API接口
繼承Resource類來寫的RESTful-API接口不能使用上述辦法。解決辦法是在需要支持跨域請求的API類下,添加一個類方法options
,就可以解決跨域問題
原因是跨域問題請求的時候,無論是POST請求還是PUT請求,前端都會優先發起一個OPTIONS請求確認請求允許范圍.那么在解決跨域請求時,只需要在每個資源url中加入options請求方式,并返回適當的響應頭信息就能解決跨域問題.
class YourAPI(Resource):def options(self):return {'Allow': '*'}, 200, {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, POST, DELETE, PUT','Access-Control-Allow-Headers': 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',}
具體可配置參數解釋:
-
Access-Control-Allow-Origin
這個頭部信息由服務器返回,用來明確指定那些客戶端的域名允許訪問這個資源。它的值可以是:-使用" * " —— 允許任意域名
- 一個完整的域名名字(比如:https://example.com)
如果你需要客戶端傳遞驗證信息到頭部(比如:cookies)。這個值不能為 * —— 必須為完整的域名(這點很重要)。
-
Access-Control-Allow-Credentials
這個頭部信息只會在服務器支持通過cookies傳遞驗證信息的返回數據里。它的值只有一個就是 true。跨站點帶驗證信息時,服務器必須要爭取設置這個值,服務器才能獲取到用戶的cookie。 -
Access-Control-Allow-Headers
提供一個逗號分隔的列表表示服務器支持的請求數據類型。假如你使用自定義頭部(比如:x-authentication-token 服務器需要在返回OPTIONS請求時,要把這個值放到這個頭部里,否則請求會被阻止)。 -
Access-Control-Expose-Headers
相似的,這個返回信息里包含了一組頭部信息,這些信息表示那些客戶端可以使用。其他沒有在里面的頭部信息將會被限制(譯者注:這個頭信息實戰中使用較少)。 -
Access-Control-Allow-Methods
一個逗號分隔的列表,表明服務器支持的請求類型(比如:GET, POST) -
Origin
這個頭部信息,屬于請求數據的一部分。這個值表明這個請求是從瀏覽器打開的哪個域名下發出的。出于安全原因,瀏覽器不允許你修改這個值。