Spring?Security中的CORS詳解
一、CORS是什么
CORS(Cross-Origin Resource Sharing,跨源/域資源共享 )是一個W3C標準,一種允許當前域(domain)的資源(比如html/js/web service)被其他域(domain)的腳本(比如AJAX)請求訪問的機制,通常由于同源安全策略,瀏覽器會禁止這種跨域請求。
CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE 瀏覽器不能低于 IE10。
整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附件的頭信息,有時還會多處一次附件的請求,但用戶不會有感覺。
因此,實現(xiàn)CORS通信的關鍵是服務器。只要服務器實現(xiàn)了CORS功能(響應報文包括了正確的CORS響應頭),就可以被跨源訪問(可以指定個別域或全部域)。
二、同源安全策略
說到跨域請求,就不得不說一下“同源安全策略”了,如果沒有這個策略的存在,也就沒有這么多事了,但是,這樣可能會造成你在網(wǎng)站進行一些操作時存在風險。
同源策略是一個重要的安全策略,它用于限制一個源/域的文檔或它加載的腳本是否能與另一個源/域的資源進行交互。它能幫助阻隔惡意文檔,減少可能被攻擊的媒介。
出于安全性,瀏覽器會限制腳本內(nèi)發(fā)起的跨域HTTP請求,例如常見的 XHR、Fetch API 都遵循同源策略。
如果兩個 URL 的協(xié)議(Protocol)、主機(Host)、端口(Port,如果有指定的話)都相同的話,那么這兩個 URL 是同源的,否則是不同源的。

當瀏覽器中打開的某個網(wǎng)頁,有腳本執(zhí)行了跨域請求,那么,瀏覽器的“同源安全策略”就會介入,大致流程如下:
瀏覽器直接發(fā)出CORS請求,也就是在頭信息之中,增加一個Origin字段。這個字段代表本次請求來自哪個源(協(xié)議 + 主機 + 端口),服務器會根據(jù)這個值,決定是否同意這次請求。前面的流程是對于HTTP簡單請求,如果是HTTP非簡單請求,則會在正式請求前,增加一次預檢請求。
如果Origin指定的源,不在許可范圍內(nèi)(服務器端CORS功能指定),服務器會返回一個正確的HTTP回應。瀏覽器發(fā)現(xiàn),這個回應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲。注意,這種錯誤無法通過狀態(tài)識別,因為HTTP回應的狀態(tài)碼有可能是 200。
拋出的錯誤為“has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.”,如下:

三、Spring Security中CORS的開啟
在Spring框架中,我們可以在引入Spring Security依賴后,對Security的HttpSecurity進行設置,來開啟CORS(跨域/源資源共享),同時能指定只被部分域/源、部分方法、部分頭部信息訪問資源。
Spring框架提供了CorsFilter,是為了在基于filter的安全框架(如Spring Security)上面支持CORS,或者在使用其他不支持CORS的庫上支持CORS。
//(Security6.2.4寫法)先創(chuàng)建一個普通JAVA類,如SecurityConfig.java,實現(xiàn)如下3個Bean。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
(authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults())
.formLogin(withDefaults())
.csrf((csrf)->csrf.disable())
.cors(withDefaults()); //開啟CORS(跨域/源資源共享)
return http.build();
}
@Autowired //自動裝配參數(shù)configurationSource(下方的Bean)
@Bean
public CorsFilter corsFilter(UrlBasedCorsConfigurationSource configurationSource){
return new CorsFilter(configurationSource);
}
// 也可以將方法內(nèi)的實現(xiàn)整合到上面的corsFilter方法體內(nèi)
@Bean
public UrlBasedCorsConfigurationSource configurationSource(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOriginPattern("http://localhost*"); //新版本寫法
//corsConfiguration.addAllowedOrigin("*"); //老版本寫法
source.registerCorsConfiguration("/**",corsConfiguration);
return source;
}四、其它處理方法
1、Spring注解
原理:自Spring Framework 4.2開始,CORS請求(包括OPTIONS method)會被自動分發(fā)到各種注冊過的HandlerMappings。它們會處理CORS的preflight請求,會攔截CORS簡單和實際請求,以便基于你指定的CORS配置,添加相關的CORS響應頭(如 Access-Control-Allow-Origin)。
實現(xiàn):在@RequestMapping注解過的controller method上面添加@CrossOrigin注解,表示已開啟CORS。
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600) //括號內(nèi)為指定的CORS配置
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com") //同時使用controller級別和method級別的CORS配置,Spring會將二者的attributes結合起來,創(chuàng)建出融合的CORS配置。
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
}除了基于注解@CrossOrigin的配置(細粒度),還可以定義全局的CORS配置。類似于使用filters,但可以定義在Spring MVC中,并與細粒度的@CrossOrigin配置相配合。
2、JSONP(JSON with Padding)
原理:利用<script>標簽不受瀏覽器同源策略限制的特性,通過動態(tài)插入<script>標簽的方式實現(xiàn)跨域數(shù)據(jù)訪問。
實現(xiàn):客戶端創(chuàng)建一個<script>標簽,將請求后端的接口URL拼接一個回調(diào)函數(shù)名稱作為參數(shù)傳給后端,并設置給<script>標簽的src屬性。后端接收到請求后,將數(shù)據(jù)和回調(diào)函數(shù)名稱拼接成函數(shù)調(diào)用的形式返回??蛻舳嗽诮邮盏巾憫螅瑫?zhí)行這個回調(diào)函數(shù),從而獲取到后端返回的數(shù)據(jù)。
3、使用代理服務器
原理:通過搭建一個代理服務器來轉發(fā)客戶端的請求,代理服務器與目標服務器進行通信,然后將返回的數(shù)據(jù)再轉發(fā)給客戶端。由于客戶端和代理服務器、代理服務器和目標服務器之間的通信都在服務器端進行,因此不受瀏覽器同源策略的限制。
實現(xiàn):可以使用Nginx、Node.js等搭建代理服務器。例如,Nginx可以通過配置反向代理來解決跨域問題;Node.js可以搭建一個中間層服務器,接收客戶端的請求,然后將請求轉發(fā)給目標服務器,并將返回的數(shù)據(jù)再轉發(fā)給客戶端。
4、前端正向代理
原理:在客戶端設置一個代理服務器,并指定目標服務器。代理服務器向目標服務器轉交請求,并將獲得的內(nèi)容發(fā)送給客戶端。這種方式在客戶端進行了請求轉發(fā),但同樣實現(xiàn)了跨域請求。
實現(xiàn):可以通過在客戶端代碼中配置代理服務器地址和端口,以及目標服務器的地址和端口來實現(xiàn)。在發(fā)送請求時,將請求地址替換為代理服務器的地址,并在代理服務器中進行請求轉發(fā)。
5、配置瀏覽器插件或工具
有些瀏覽器插件或工具(如Postman、curl等)可以繞過瀏覽器的同源策略限制,直接發(fā)送跨域請求。但這種方法主要用于開發(fā)和測試階段,不建議在生產(chǎn)環(huán)境中使用。
補充:關于Spring Security的CSRF
關于Spring Security的CSRF
一、CSRF是什么
CSRF(Cross-Site Request Forgery,跨站請求偽造)是一種常見的網(wǎng)絡攻擊方式,攻擊者通過偽裝用戶的請求,利用用戶在其他網(wǎng)站上已經(jīng)登錄的身份權限來執(zhí)行惡意操作。為了防止 CSRF 攻擊,Spring Security 提供了內(nèi)置的 CSRF 保護功能。
二、Spring Security中的CSRF保護
在Spring框架中,如果引入了Spring Security依賴,除了會默認開啟身份驗證,同時還會開啟CSRF保護功能。
啟用此功能可確保,在會更改狀態(tài)的HTTP請求上,如果沒有帶上有效的X-CSRFToken,則會出現(xiàn) 403 錯誤。
HTTP狀態(tài)碼403表示禁止訪問,即服務器理解請求客戶端的請求,但是拒絕執(zhí)行這個請求。這通常是由于客戶端沒有足夠的權限訪問該資源所導致的。
可以認為安全的方法都是只讀的方法(GET, HEAD, OPTIONS),這些類型的請求不會改變資源狀態(tài),被認為是冪等的,即調(diào)用相同的URL多次得到的結果不變。
DELETE方法的語義表示刪除服務器上的一個資源,第一次刪除成功后該資源就不存在了,資源狀態(tài)改變了,所以DELETE方法不具備安全特性。然而HTTP協(xié)議規(guī)定DELETE方法是冪等的,每次刪除該資源都要返回狀態(tài)碼200 OK,服務器端要實現(xiàn)冪等的DELETE方法,必須記錄所有已刪除資源的元數(shù)據(jù)(Metadata),否則,第二次刪除后返回的響應碼就會類似404 Not Found了。
PUT和POST方法語義中都有修改資源狀態(tài)的意思,因此都不是安全的。但是PUT方法是冪等的,POST方法不是冪等的,這么設計的理由是:
HTTP協(xié)議規(guī)定,POST方法修改資源狀態(tài)時,URL指示的是該資源的父級資源,待修改資源的ID信息在請求體中攜帶。而PUT方法修改資源狀態(tài)時,URL直接指示待修改資源。因此,同樣是創(chuàng)建資源,重復提交POST請求可能產(chǎn)生兩個不同的資源,而重復提交PUT請求只會對其URL中指定的資源起作用,也就是只會創(chuàng)建一個資源。
因此,當Spring Security使用默認配置,或手動開啟CSRF保護功能時,我們會發(fā)現(xiàn),GET請求是正常的,但是POST請求會報403錯誤。
三、處理方法
處理方法一:跳過安全檢查(不推薦,因為這個一般是用于靜態(tài)資源等)
@Bean
//(Spring Security6.2.4寫法)
public WebSecurityCustomizer webSecurityCustomizer() {
// 匹配的路徑,直接跳過安全檢查
return (web) -> web.ignoring().requestMatchers("/static/**");
}處理方法二:關閉CSRF保護功能
@Bean
//(Spring Security6.2.4寫法)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
(authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults())
.formLogin(withDefaults())
.csrf((csrf)->csrf.disable());
//只對匹配路徑關閉CSRF
//.csrf((csrf)->csrf.ignoringRequestMatchers("/路徑"));
return http.build();
}處理方法三:正常在請求時帶上X-CSRFToken
到此這篇關于關于Spring Security的CORS的文章就介紹到這了,更多相關Spring Security的CORS內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Java把文本內(nèi)容轉換成網(wǎng)頁的實現(xiàn)方法分享
這篇文章主要介紹了使用Java把文本內(nèi)容轉換成網(wǎng)頁的實現(xiàn)方法分享,利用到了Java中的文件io包,需要的朋友可以參考下2015-11-11
Springboot PostMapping無法獲取數(shù)據(jù)問題及解決
這篇文章主要介紹了Springboot PostMapping無法獲取數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
Java中遇到的For?input?string問題解決辦法
這篇文章主要給大家介紹了關于Java中遇到的For?input?string問題的解決辦法,如果出現(xiàn)這樣的異常報錯,是指的數(shù)據(jù)轉換時出錯,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2023-11-11
springboot使用GuavaCache做簡單緩存處理的方法
這篇文章主要介紹了springboot使用GuavaCache做簡單緩存處理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
Javaweb開發(fā)中通過Servlet生成驗證碼圖片
這篇文章主要為大家詳細介紹了Javaweb開發(fā)中通過Servlet生成驗證碼圖片的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05

