天天看點

CORS實作跨域

同源政策

很多人對跨域有一種誤解,以為這是前端的事,和後端沒關系,其實不是這樣的,說到跨域,就不得不說說浏覽器的同源政策。

同源政策是由Netscape提出的一個著名的安全政策,它是浏覽器最核心也最基本的安全功能,現在所有支援JavaScript的浏覽器都會使用這個政策。所謂同源是指協定、域名以及端口要相同。同源政策是基于安全方面的考慮提出來的,這個政策本身沒問題,但是我們在實際開發中,由于各種原因又經常有跨域的需求,傳統的跨域方案是JSONP,JSONP雖然能解決跨域但是有一個很大的局限性,那就是隻支援GET請求,不支援其他類型的請求,而今天我們說的CORS(跨域源資源共享)(CORS,Cross-origin resource sharing)是一個W3C标準,它是一份浏覽器技術的規範,提供了Web服務從不同網域傳來沙盒腳本的方法,以避開浏覽器的同源政策,這是JSONP模式的現代版。

在Spring架構中,對于CORS也提供了相應的解決方案,今天我們就來看看SpringBoot中如何實作CORS。

實踐

接下來我們就來看看SpringBoot中如何實作這個東西。

首先建立兩個普通的SpringBoot項目,這個就不用我多說,第一個命名為provider提供服務,第二個命名為consumer消費服務,第一個配置端口為8080,第二個配置配置為8081,然後在provider上提供兩個hello接口,一個get,一個post,如下:

@RestController
public 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注解配置某一個方法接受某一個域的請求,如下:

@RestController
public 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也能拿到資料了。

此時觀察浏覽器請求網絡控制台,可以看到響應頭中多了如下資訊:

CORS實作跨域

這個表示服務端願意接收來自http://localhost:8081的請求,拿到這個資訊後,浏覽器就不會再去限制本次請求的跨域了。

provider上,每一個方法上都去加注解未免太麻煩了,在Spring Boot中,還可以通過全局配置一次性解決這個問題,全局配置隻需要在配置類中重寫addCorsMappings方法即可,如下

@Configuration
public 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位址如下:http://icbc.com/aa?bb=cc,那麼,一個惡意攻擊者可以在另一個網站上放置如下代碼:,如果使用者通路了惡意站點,而她之前剛通路過銀行不久,登入資訊尚未過期,那麼她就會遭受損失。基于此,浏覽器在實際操作中,會對請求進行分類,分為簡單請求,預先請求,帶憑證的請求等,預先請求會首先發送一個options探測請求,和浏覽器進行協商是否接受請求。預設情況下跨域請求是不需要憑證的,但是服務端可以配置要求用戶端提供憑證,這樣就可以有效避免csrf攻擊。

繼續閱讀