記錄我開發工做中遇到HTTP跨域和OPTION請求的一個坑

我經過這篇文章把今天工做中遇到的HTTP跨域和OPTION請求的一個坑記錄下來。javascript

場景是我須要在部署在域名a的Web應用裏用JavaScript去消費一個部署在域名b的服務器上的服務。域名b上的服務也是我開發的,所以我將域名a加到了該服務的HTTP響應結構的頭文件裏,這樣就容許了域名a上的JavaScript代碼用AJAX訪問域名b的服務。html

域名b上的服務是一個Servlet,容許域名a跨域訪問的代碼就一行:前端

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 作業務邏輯

         response.setHeader("Access-Control-Allow-Origin", "域名a");

}

我在域名a的Web應用裏用AJAX發起服務請求:java

執行後,發現並無顯示200的彈出窗口。跨域

錯誤消息:Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.瀏覽器

觀察Chrome開發者工具,發現其實域名b的服務已經成功執行了,確實返回了200的Status code,安全

並且我已經從Chrome開發者工具裏觀察到瀏覽器已經成功接到域名b發送回來的請求了。服務器

那這個錯誤是什麼鬼呢?根據錯誤消息「Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response」 Google了一下,發現一些朋友遇到一樣的問題:app

1. 如何解決出現AXIOS的Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.cors

網頁地址: http://www.javashuo.com/article/p-xcprhdot-gt.html

這位朋友的解決方案:

response.setHeader("Access-Control-Allow-Origin", "*");

response.setHeader("Access-Control-Allow-Credentials", "true");

response.setHeader("Access-Control-Allow-Methods", "*");

response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");

response.setHeader("Access-Control-Expose-Headers", "*");

if (request.getMethod().equals("OPTIONS")) {

     HttpUtil.setResponse(response, HttpStatus.OK.value(), null);

     return;

}

但我試過,在個人場景下仍是不工做,由於個人例子裏,服務器已經針對OPTIONS請求返回HTTP 200的狀態碼了。

2. 這個Stackoverflow的帖子裏,不少朋友都提供了本身的解決方案。

https://stackoverflow.com/questions/42061727/cors-error-request-header-field-authorization-is-not-allowed-by-access-control/42061962

我一一試過,在個人場景裏都不能工做。

因而我查詢了Mozilla的一篇文檔:HTTP訪問控制(CORS)

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

裏面談到了,在某些狀況下,瀏覽器在發起「須要預檢的請求」以前,必須首先發起一個「預檢請求(Preflight)」到服務器,以探測服務器是否容許這個實際請求。"預檢請求"機制的使用,是爲了不跨域請求對服務器的用戶數據產生未預期的影響。

那麼哪些請求算做「須要預檢的請求」呢?Mozilla的這篇文檔定義得很清楚:

當請求知足下述任一條件時,即應首先發送預檢請求:

  • 使用了下面任一 HTTP 方法:
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH
  • 人爲設置了對 CORS 安全的首部字段集合以外的其餘首部字段。該集合爲:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (but note the additional requirements below)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width
  • Content-Type 的值不屬於下列之一:
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

我再檢查個人代碼,由於我在HTTP請求裏用xhr.setRequestHeader("Authorization", "用戶名:密碼的base64編碼" )添加了用於Basic Authentication的頭部,所以迫使該請求成爲了「須要預檢的請求」,因此纔有了OPTION請求的發送。

如今我將其註釋掉:

此次遇到了401 Unauthorized錯誤了:

然而沒有預檢請求OPTION發出來了,請求類型變成了我指望的POST方式了。

可是如今就陷入了一個矛盾的境地:若是在請求頭部加上Basic Authentication的信息,會遇到錯誤消息「Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.」。若是去掉,雖然避免了預檢請求,可是又遇到401 Unauthorized錯誤了。

因而,我換了一種認證方式,終於成功實現了指望的跨域請求,在我域名a的前端應用裏打印出了來自於域名b的服務的響應。

我使用了form認證方式,這種方式不會形成該請求成爲一個」須要預檢的請求「,因此最後跨域成功了。

var formData = new FormData();

formData.append('sap-client', "001");

formData.append('sap-user', "用戶名");

formData.append('sap-password', "用戶密碼");

var request = new XMLHttpRequest();

request.open("POST", "域名b的url",false);

request.send(formData);

alert("response: " + request.responseText);

但願個人這個踩坑經歷對你們有點幫助。

要獲取更多Jerry的原創技術文章,請關注公衆號"汪子熙"或者掃描下面二維碼:

相關文章
相關標籤/搜索