预检请求
不久前在公司寫了一個基于 Hapijs 的后端項目,感覺這個框架很有自己的特點,跟 Express 和 Koa 的區(qū)別比較大,體現(xiàn)了配置大于編碼的思想。用起來很方便,據(jù)說 Walmart 團隊用這個框架扛住了黑五的流量,看起來在實際項目中也有可用性,推薦大家嘗試一下~
有點跑題了,這篇文章主要寫我在開發(fā)過程中所遇到的一個問題,以及我從這個問題所學習到的東西,然后我是怎么解決這個問題的。
一、問題
我的項目需求是寫一個 App 版本管理器,前后端都由我開發(fā)。前端分為兩個部分:運營人員寫版本更新說明的內部系統(tǒng)和 App 訪問的產(chǎn)品頁;后端就是對 App 版本進行管理的 CURD 接口。重點在于三個部分的程序部署在三臺服務器上,前端的兩個系統(tǒng)在不同的服務器對第三個服務器上的接口進行數(shù)據(jù)請求,這就不可避免的涉及到了跨域。
當然,只是跨域的話也不難解決,添加 Access-Control-Allow-Origin 為要跨域的域名就 OK 了,或者直接賦值為 *。但是我的部分接口涉及鑒權,通過 JWT 進行校驗,如果 JWT 不合法,那么會返回 401 Unauthorized 錯誤;而我的 JWT 是通過請求頭的自定義字段 authorization 帶到服務器的,這就導致一個更加麻煩的問題出現(xiàn)了 —— 預檢請求。
二、收獲
什么是預檢請求?
預檢請求(preflight request),是一個跨域請求,用來校驗當前跨域請求能否被理解。
它使用 HTTP 的 OPTIONS 請求,一般會包括一下請求頭:Access-Control-Request-Method,Access-Control-Request-Headers 和 Origin。
預檢請求通常在必要的時候由瀏覽器自動發(fā)起,不需要程序員進行干預。
如果我們想要知道服務器是否支持一個 DELETE 請求,在發(fā)送 DELETE 請求之前,服務器通常會發(fā)送一個如下的預檢請求:
OPTIONS /resource/foo Access-Control-Request-Method: DELETE Access-Control-Request-Headers: origin, x-requested-with Origin: https://foo.bar.org如果服務器允許使用 DELETE 方法的話,會返回如下響應頭;其中 Access-Control-Allow-Methods 會列出 DELETE 方法,代表服務器支持這個方法。
HTTP/1.1 200 OK Content-Length: 0 Connection: keep-alive Access-Control-Allow-Origin: https://foo.bar.org Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE Access-Control-Max-Age: 86400以上資料來源于 MDN
由此可知,預檢請求是一個用于校驗服務器是否支持當前方法以及是否能夠理解當前請求的一種請求,它區(qū)別于一般的請求,不由代碼發(fā)起,而在必要的時候由瀏覽器自動發(fā)出。
所以這里就出問題了,如果我們不知道什么時候瀏覽器會發(fā)出預檢請求,那么服務器沒有做處理的話就會導致 CORS 報錯的出現(xiàn)。
接下來再深入一點。
預檢請求與普通請求的區(qū)別
滿足以下條件的請求就是簡單請求:
一、請求方法屬于下面三種方法之一:
- HEAD
- POST
- GET
二、HTTP 的請求頭信息超出一下范圍:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
Content-Type:超出這三個的范圍:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
不滿足以上條件的請求就是非簡單請求。
如果是簡單的 CORS 請求,瀏覽器會自動在請求頭中添加一個 Origin 請求頭字段,如果響應頭對應的 Access-Control-Allow-Origin 沒有包含 Origin 所指定的域,那么就會報 CORS 錯誤,請求失敗。所以服務器的響應要添加對應的響應頭。
如果是非簡單的 CORS 請求,那么會有一次預檢請求,在正是請求之前發(fā)出一個 OPTIONS 請求對服務器進行檢測。
除了有 Origin 以外,預檢請求的請求頭還包括一下兩個特殊字段:
Access-Control-Request-Method:表示 CORS 請求要用到的請求方法。
Access-Control-Request-Headers:這是一個用逗號分割的字符串,指出 CORS 請求要附加的請求頭。
服務器的響應可以包含以下字段:
Access-Control-Allow-Methods:逗號分割的字符串,表示允許的跨域請求方法。
比如:
Access-Control-Allow-Methods: PUT, POST, GET, OPTIONSAccess-Control-Allow-Headers:如果瀏覽器請求包含 Access-Control-Request-Headers 字段,那么服務器中該響應頭也是必須的,也是一個由逗號分隔的字符串,表示服務器支持的請求頭。
比如:
Access-Control-Allow-Headers: authorizationAccess-Control-Max-Age:可選字段,設置當前預檢請求的有效期,單位為秒。
Access-Control-Allow-Credentials:可選字段。默認情況下,CORS 請求不攜帶 cookie,如果服務器想要 cookie,需要指定該請求頭為 true。
三、解決方法
避免出現(xiàn)預檢請求,需要使得你的請求滿足簡單請求的兩個條件。
比如在使用 JWT 鑒權時,可能會把你的 token 放在請求頭的 authorization 字段,因為這個字段超出了簡單請求的范圍,所以請求會變成非簡單請求。這時可以不把 token 放在 authorization 請求頭中。
出現(xiàn)預檢請求后,進行服務器配置,分別設置好 Access-Control-Allow-Origin、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers,使得你的非簡單請求能夠通過預檢請求。
如果使用 Hapijs 的話,只需要在路由配置中增加 cors: true 配置即可。
參考
- MDN
- 跨域資源共享 CORS 詳解
- cors跨域之簡單請求與預檢請求(發(fā)送請求頭帶令牌token)
轉載于:https://www.cnblogs.com/DM428/p/10304971.html
總結
- 上一篇: HTML学习之基础
- 下一篇: 微星主板黑苹果_黑苹果安装教程:准备磁盘