跨域你需要知道這些
跨域無疑是一個老生常談的話題了,而且也是每個前端同學都會遇到的問題,那麼跨域你真的講的清嗎,不妨看看這些
一、什麼是跨域?
由於瀏覽器同源策略,凡是傳送請求url的協議、域名、埠三者之間任意一個與當前頁面地址不同即為跨域
1、同源策略:
- 同源策略是 Netscape 提出的一個著名的安全策略
- 同源策略是瀏覽器最核心最基礎的安全策略
- 現在所有的可支援 Javascript 的瀏覽器都會使用這個策略
- web構建在同源策略基礎之上,瀏覽器對非同源指令碼的限制措施是對同源策略的具體實現
2、同源策略含義:
- DOM 層面的同源策略:限制了來自不同源的”Document”物件或 JS 指令碼,對當前“document”物件的讀取或設定某些屬性
- Cookie和XMLHttprequest層面的同源策略:禁止 Ajax 直接發起跨域HTTP請求(其實可以傳送請求,只是結果被瀏覽器攔截,不展示),同時 Ajax 請求不能攜帶與本網站不同源的 Cookie。
- 同源策略的非絕對性:
<script><img><iframe><link><video><audio>
等帶有src屬性的標籤可以從不同的域載入和執行資源。 - 其他外掛的同源策略:
flash、java applet、silverlight、googlegears
等瀏覽器載入的第三方外掛也有各自的同源策略,只是這些同源策略不屬於瀏覽器原生的同源策略,如果有漏洞則可能被黑客利用,從而留下XSS攻擊的後患
二、為什麼會跨域?
瀏覽器之所以要對跨域請求作出限制,是出於安全方面的考慮,防止被不法分子利用利用跨域請求來發動 CSRF攻擊 。
三、跨域解決方案
1、JSONP
在jQuery中最常用的就是Ajax 的JSONP了,利用 <script><img><iframe>
等標籤不受同源策略限制,可以從不同域載入並執行資源的特性,來實現資料跨域傳輸。JSONP由回撥函式和資料兩部分組成,使用與服務端約定好回撥函式名稱,服務端接收到請求後返回一段JavaScript程式碼,這段程式碼呼叫約定好的回撥函式,並將資料作為引數進行傳遞,頁面接收到這段程式碼後就會立即執行這個回撥函式,解析傳遞來的資料
2、NGINX代理
代理是用於將請求傳送給後臺伺服器,通過伺服器來發送請求,然後將請求的結果傳遞給前端
注意點:如果代理的是https協議的請求,那麼你的proxy首先需要信任該證書(尤其是自定義證書)或者忽略證書檢查,否則請求無法成功。
3、CORS
CORS即跨源資源共享 Cross-Origin Resource Sharing(CORS),是一個新的W3C標準,允許服務端宣告那些網站有許可權訪問哪些資源,即允許瀏覽器向聲明瞭CORS的跨域伺服器,發出XMLHttpReuest請求,從而解決同源策略的限制,本文則著重講解CORS辦法
當非簡單請求瀏覽器發生跨域時,我們會看到瀏覽器有一個OPTION 型別的請求,這叫預檢請求,在規範中規定,對於非簡單的請求,瀏覽器必須首先使用 OPTION 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求,在伺服器確定允許後,才發起實際的HTTP請求。對於簡單請求、非簡單請求以及預檢請求的詳細資料可以閱讀,HTTP訪問控制
那麼怎麼避免觸發預檢請求呢
上文說了,非簡單請求才會觸發預檢請求,所以只要保證請求是簡單請求即可,注意這裡的簡單請求不單單指請求方式,詳情請看簡單請求,簡單請求的請求方式包含GET、POST、HEAD,但是在實際中POST請求也會預檢請求,那是因為只有當請求的Content-Type值是 text/plain,
multipart/form-data,
application/x-www-form-urlencoded三者之一時才被認為是簡單請求
下面以Vue + axios專案例項做跨域講解
在Vue中當axios配置 axios.defaults.baseURL ='******'後,本地代理就不再生效,那麼想要解決跨域的問題首先需要後端服務允許跨域,然後前端保證發出的是簡單請求
後端服務配置:
protected void writeStr(HttpServletResponse resp,HttpServletRequest request, String str) { resp.setHeader("Access-Control-Allow-Origin", "*"); resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); resp.setHeader("Access-Control-Max-Age", "3600"); Enumeration<String> headersEnum = ((HttpServletRequest) request).getHeaders("Access-Control-Request-Headers"); StringBuilder headers = new StringBuilder(); String delim = "";
//取消OPTIONS預檢請求 while (headersEnum.hasMoreElements()) { headers.append(delim).append(headersEnum.nextElement()); delim = ", "; } resp.setHeader("Access-Control-Allow-Headers", headers.toString()); resp.setContentType("text/html; charset=UTF-8"); try { resp.getWriter().write(str); LoggerUtils.getLogger().info("==================="); LoggerUtils.getLogger().info("return msg:{}", str); } catch(IOException e) { LoggerUtils.getLogger().error(e.getMessage(),e); } }
Access-Control-Allow-Origin
響應首部中可以攜帶這個頭部表示伺服器允許哪些域或者所有域可以訪問該資源,語法: Access-Control-Allow-Origin: <origin> | * ,origin 引數的值指定了允許訪問該資源的外域 URI。對於不需要攜帶身份憑證的請求,伺服器可以指定該欄位的值為萬用字元,表示允許來自所有域的請求。
Access-Control-Allow-Methods
該首部欄位用於預檢請求的響應,指明實際請求所允許使用的HTTP方法。語法: Access-Control-Allow-Methods: <method>[, <method>]*,
Access-Control-Max-Age
該首部欄位用於預檢請求的響應,指定了預檢請求能夠被快取多久,語法: Access-Control-Max-Age: <delta-seconds>
,
前端配置:
保證前端請求即可。未完待續......