spring boot 中通過CORS實現跨域

  • 2019 年 10 月 26 日
  • 筆記

一、跨域問題出現的原因 

  出現跨域問題的原因是同源策略。

  同源策略

  主要是三同:同協議、同域名、同埠,

  同源策略目的

  • 保證用戶資訊安全,防止惡意網站竊取數據。同源策略是必須的,否則cookie可以共享。

  同源策略的限制範圍

  • cookie、localstorage、indexdb無法讀取。
  • DOM無法獲取。
  • ajax請求不能發送。

  規避策略

  1. cookie:設置document.domain共享cookie。
  2. DOM獲取:(父子頁面通訊)H5引入了一個API,這個API為windows對象新增了一個window.postMessage方法,允許跨窗口通訊,無論這兩個窗口是否同源。
  3. window.opener.postMessage(content,origin)    content是消息的具體內容,origin是協議 + 域名 + 埠

  1. AJAX:
  • JSONP:JSONP是伺服器無客戶端跨源通訊的常用方法。基本思想是網頁通過添加一個<script>元素,向伺服器請求json數據,這種做法不受同源政策的限制,伺服器收到請求後,將數據放在一個指定名字的回調函數裡面傳回來。(只能發GET請求)
  • WebSocket:WebSocket是一種通訊協議。使用ws://(非加密)和wss://(加密)作為協議前綴。該協議不實行同源政策,只要伺服器支援,就可以通過它進行跨源通訊。
  • CORS:詳解如下。

  跨域資源共享(corss-origin resource sharing):CORS需要瀏覽器和伺服器同時支援。目前所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。整個CORS通訊過程,都是瀏覽器自動完成,不需要用戶參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭資訊,有時還會多出一次附加的請求,但用戶不會有感覺。因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

  兩種請求

  • 瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

  CORS請求

  1. 簡單請求

  • 對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在Header中增加一個Origin欄位。如果瀏覽器發現跨源AJAX請求是簡單請求,就自動在頭資訊之中,添加一個Origin欄位。
GET /cors HTTP/1.1  Origin: http://localhost:8081     //瀏覽器添加欄位,說明本次請求來自哪個源(協議+域名+埠)。  Host: 119.23.214.114  Accept-Language: en-US  Connection: keep-alive  User-Agent: Mozilla/5.0...

  • 如果Origin指定的源在不在後端的許可白名單範圍內,伺服器會返回一個正常的http回應。瀏覽器接收後發現,這個response的Header沒有包含Access-Control-Allow-Origin欄位,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。這種錯誤無法通過狀態碼識別,因此HTTP response的狀態碼有可能是200。

  2. 非簡單請求

  • 非簡單請求是那種對伺服器有特殊要求的請求,比如請求方法是PUTDELETE,或者Content-Type欄位的類型是application/json
  • 非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為”預檢”請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。

  以上內容鏈接:https://www.jianshu.com/p/413a2f11828d

二、spring boot 通過CORS解決跨域問題

  先創建一個項目server作為伺服器端,默認埠為8080,並在服務端提供兩個介面:

//方便client分別發送不同類型請求的時候可以 觀察一下簡單請求和非簡單請求的區別
@RestController
public class HelloController { @GetMapping("gethello") //get 類型public String getHello(){ return "get hello"; } @PutMapping("puthello") //put類型 public String putHello(){ return "put hello"; } }

  再創建一個項目client作為請求端,並設置請求端的埠為8081:

server.port=8081

在請求端的resources/static目錄下增加本次訪問所需要的頁面index.html,記得同時自己找一個jquery.min.js 也放到static目錄下:

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <script src="jquery.min.js"></script>  </head>  <body>  <div id="div"></div>  <input type="button" value="獲取" onclick="gethello()"> //用於測試簡單請求  <input type="button" value="更新" onclick="puthello()"> //用戶測試非簡單請求  <script>      function gethello() {          $.get('http://localhost:8080/gethello',function (msg) {              $("#div").html(msg); //將調用介面返回值在頁面上顯示          });      }      function puthello() {         $.ajax({             type:'put',             url:'http://localhost:8080/puthello',             success:function (msg) {                 $("#div").html(msg);             }         })      }  </script>  </body>  </html><!DOCTYPE html>

    接下來看看跨域失敗的的時候:首先啟動server端,再啟動client端。在瀏覽器中訪問:http://localhost:8081/index.html,點擊獲取按鈕:

  然後我們在服務端中通過CORS解決跨域的問題,有兩種解決辦法。

  1.在介面類或者方法上面添加 @CrossOrigin註解

   @GetMapping("gethello")      @CrossOrigin(value = "http://localhost:8081") //指定可以訪問的白名單      public String getHello(){          return "get hello";      }

  2.實現WebMvcConfigurer介面重寫addCorsMappings方法


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {      @Override      public void addCorsMappings(CorsRegistry registry) {         registry.addMapping("/**").allowedOrigins("http://localhost:8081")                  .allowedMethods("*")                  .allowedHeaders("*")
          
.maxAge(30000);
} }

   解決了跨域問題後,重啟服務端:再進行獲取和更新操作:

  1.獲取操作 成功

  

  2.更新操作

  正如前面所說的非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為”預檢”請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。