轉自:江南一點雨
今天和小夥伴們來聊一聊通過CORS解決跨域問題。
同源策略
很多人對跨域有一種誤解,以為這是前端的事,和後端沒關係,其實不是這樣的,說到跨域,就不得不說說瀏覽器的同源策略。
同源策略是由Netscape提出的一個著名的安全策略,它是瀏覽器最核心也最基本的安全功能,現在所有支援JavaScript的瀏覽器都會使用這個策略。所謂同源是指協議、域名以及埠要相同。同源策略是基於安全方面的考慮提出來的,這個策略本身沒問題,但是我們在實際開發中,由於各種原因又經常有跨域的需求,傳統的跨域方案是JSONP,JSONP雖然能解決跨域但是有一個很大的侷限性,那就是隻支援GET請求,不支援其他型別的請求,而今天我們說的CORS(跨域源資源共享)(CORS,Cross-origin resource sharing)是一個W3C標準,它是一份瀏覽器技術的規範,提供了Web服務從不同網域傳來沙盒指令碼的方法,以避開瀏覽器的同源策略,這是JSONP模式的現代版。
在Spring框架中,對於CORS也提供了相應的解決方案,今天我們就來看看SpringBoot中如何實現CORS。
實踐
接下來我們就來看看Spring Boot中如何實現這個東西。
首先建立兩個普通的SpringBoot專案,這個就不用我多說,第一個命名為provider提供服務,第二個命名為consumer消費服務,第一個配置埠為8080,第二個配置配置為8081,然後在provider上提供兩個hello介面,一個get,一個post,如下:
@RestControllerpublic class HelloController { @GetMapping("/hello") public String hello() { return "hello"; } @PostMapping("/hello") public String hello2() { return "post hello"; }}
在consumer的resources/static目錄下建立一個html檔案,傳送一個簡單的ajax請求,如下:
<div id="app"></div><input type="button" onclick="btnClick()" value="get_button"><input type="button" onclick="btnClick2()"value="post_button"><script> function btnClick() { $.get('http://localhost:8080/hello', function (msg) { $("#app").html(msg); }); } function btnClick2() { $.post('http://localhost:8080/hello', function (msg) { $("#app").html(msg); }); }</script>
Access to XMLHttpRequest at 'http://localhost:8080/hello' from origin' http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
可以看到,由於同源策略的限制,請求無法傳送成功。
使用CORS可以在前端程式碼不做任何修改的情況下,實現跨域,那麼接下來看看在provider中如何配置。首先可以通過@CrossOrigin註解配置某一個方法接受某一個域的請求,如下:
@RestControllerpublic class HelloController { @CrossOrigin(value = "http://localhost:8081") @GetMapping("/hello") public String hello() { return "hello"; } @CrossOrigin(value = "http://localhost:8081") @PostMapping("/hello") public String hello2() { return "post hello"; }}
這個註解表示這兩個介面接受來自http://localhost:8081地址的請求,配置完成後,重啟provider,再次傳送請求,瀏覽器控制檯就不會報錯了,consumer也能拿到資料了。
此時觀察瀏覽器請求網路控制檯,可以看到響應頭中多了如下資訊:
這個表示服務端願意接收來自http://localhost:8081的請求,拿到這個資訊後,瀏覽器就不會再去限制本次請求的跨域了。
provider上,每一個方法上都去加註解未免太麻煩了,在Spring Boot中,還可以通過全域性配置一次性解決這個問題,全域性配置只需要在配置類中重寫addCorsMappings方法即可,如下:
@Configurationpublic class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("*") .allowedHeaders("*"); }}
/**表示本應用的所有方法都會去處理跨域請求,allowedMethods表示允許通過的請求數,allowedHeaders則表示允許的請求頭。經過這樣的配置之後,就不必在每個方法上單獨配置跨域了。
存在的問題
了解了整個CORS的工作過程之後,我們通過Ajax傳送跨域請求,雖然使用者體驗提高了,但是也有潛在的威脅存在,常見的就是CSRF(Cross-site request forgery)跨站請求偽造。跨站請求偽造也被稱為one-click attack 或者 session riding,通常縮寫為CSRF或者XSRF,是一種挾制使用者在當前已登入的Web應用程式上執行非本意的操作的攻擊方法,舉個例子:
假如一家銀行用以執行轉賬操作的URL地址如下:/file/2020/02/10/20200210151407_1919.jpg <imgsrc="/file/2020/02/10/20200210151429_1922.jpg