Spring?Security中的CORS詳解
一、CORS是什么
CORS(Cross-Origin Resource Sharing,跨源/域資源共享 )是一個W3C標(biāo)準(zhǔn),一種允許當(dāng)前域(domain)的資源(比如html/js/web service)被其他域(domain)的腳本(比如AJAX)請求訪問的機(jī)制,通常由于同源安全策略,瀏覽器會禁止這種跨域請求。
CORS需要瀏覽器和服務(wù)器同時支持。目前,所有瀏覽器都支持該功能,IE 瀏覽器不能低于 IE10。
整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附件的頭信息,有時還會多處一次附件的請求,但用戶不會有感覺。
因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS功能(響應(yīng)報文包括了正確的CORS響應(yīng)頭),就可以被跨源訪問(可以指定個別域或全部域)。
二、同源安全策略
說到跨域請求,就不得不說一下“同源安全策略”了,如果沒有這個策略的存在,也就沒有這么多事了,但是,這樣可能會造成你在網(wǎng)站進(jìn)行一些操作時存在風(fēng)險。
同源策略是一個重要的安全策略,它用于限制一個源/域的文檔或它加載的腳本是否能與另一個源/域的資源進(jìn)行交互。它能幫助阻隔惡意文檔,減少可能被攻擊的媒介。
出于安全性,瀏覽器會限制腳本內(nèi)發(fā)起的跨域HTTP請求,例如常見的 XHR、Fetch API 都遵循同源策略。
如果兩個 URL 的協(xié)議(Protocol)、主機(jī)(Host)、端口(Port,如果有指定的話)都相同的話,那么這兩個 URL 是同源的,否則是不同源的。
當(dāng)瀏覽器中打開的某個網(wǎng)頁,有腳本執(zhí)行了跨域請求,那么,瀏覽器的“同源安全策略”就會介入,大致流程如下:
瀏覽器直接發(fā)出CORS請求,也就是在頭信息之中,增加一個Origin
字段。這個字段代表本次請求來自哪個源(協(xié)議 + 主機(jī) + 端口),服務(wù)器會根據(jù)這個值,決定是否同意這次請求。前面的流程是對于HTTP簡單請求,如果是HTTP非簡單請求,則會在正式請求前,增加一次預(yù)檢請求。
如果Origin
指定的源,不在許可范圍內(nèi)(服務(wù)器端CORS功能指定),服務(wù)器會返回一個正確的HTTP回應(yīng)。瀏覽器發(fā)現(xiàn),這個回應(yīng)的頭信息沒有包含Access-Control-Allow-Origin
字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest
的onerror
回調(diào)函數(shù)捕獲。注意,這種錯誤無法通過狀態(tài)識別,因?yàn)镠TTP回應(yīng)的狀態(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進(jìn)行設(shè)置,來開啟CORS(跨域/源資源共享),同時能指定只被部分域/源、部分方法、部分頭部信息訪問資源。
Spring框架提供了CorsFilter,是為了在基于filter的安全框架(如Spring Security)上面支持CORS,或者在使用其他不支持CORS的庫上支持CORS。
//(Security6.2.4寫法)先創(chuàng)建一個普通JAVA類,如SecurityConfig.java,實(shí)現(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)的實(shí)現(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簡單和實(shí)際請求,以便基于你指定的CORS配置,添加相關(guān)的CORS響應(yīng)頭(如 Access-Control-Allow-Origin
)。
實(shí)現(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結(jié)合起來,創(chuàng)建出融合的CORS配置。 @RequestMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } }
除了基于注解@CrossOrigin的配置(細(xì)粒度),還可以定義全局的CORS配置。類似于使用filters,但可以定義在Spring MVC中,并與細(xì)粒度的@CrossOrigin配置相配合。
2、JSONP(JSON with Padding)
原理:利用<script>標(biāo)簽不受瀏覽器同源策略限制的特性,通過動態(tài)插入<script>標(biāo)簽的方式實(shí)現(xiàn)跨域數(shù)據(jù)訪問。
實(shí)現(xiàn):客戶端創(chuàng)建一個<script>標(biāo)簽,將請求后端的接口URL拼接一個回調(diào)函數(shù)名稱作為參數(shù)傳給后端,并設(shè)置給<script>標(biāo)簽的src屬性。后端接收到請求后,將數(shù)據(jù)和回調(diào)函數(shù)名稱拼接成函數(shù)調(diào)用的形式返回??蛻舳嗽诮邮盏巾憫?yīng)后,會執(zhí)行這個回調(diào)函數(shù),從而獲取到后端返回的數(shù)據(jù)。
3、使用代理服務(wù)器
原理:通過搭建一個代理服務(wù)器來轉(zhuǎn)發(fā)客戶端的請求,代理服務(wù)器與目標(biāo)服務(wù)器進(jìn)行通信,然后將返回的數(shù)據(jù)再轉(zhuǎn)發(fā)給客戶端。由于客戶端和代理服務(wù)器、代理服務(wù)器和目標(biāo)服務(wù)器之間的通信都在服務(wù)器端進(jìn)行,因此不受瀏覽器同源策略的限制。
實(shí)現(xiàn):可以使用Nginx、Node.js等搭建代理服務(wù)器。例如,Nginx可以通過配置反向代理來解決跨域問題;Node.js可以搭建一個中間層服務(wù)器,接收客戶端的請求,然后將請求轉(zhuǎn)發(fā)給目標(biāo)服務(wù)器,并將返回的數(shù)據(jù)再轉(zhuǎn)發(fā)給客戶端。
4、前端正向代理
原理:在客戶端設(shè)置一個代理服務(wù)器,并指定目標(biāo)服務(wù)器。代理服務(wù)器向目標(biāo)服務(wù)器轉(zhuǎn)交請求,并將獲得的內(nèi)容發(fā)送給客戶端。這種方式在客戶端進(jìn)行了請求轉(zhuǎn)發(fā),但同樣實(shí)現(xiàn)了跨域請求。
實(shí)現(xiàn):可以通過在客戶端代碼中配置代理服務(wù)器地址和端口,以及目標(biāo)服務(wù)器的地址和端口來實(shí)現(xiàn)。在發(fā)送請求時,將請求地址替換為代理服務(wù)器的地址,并在代理服務(wù)器中進(jìn)行請求轉(zhuǎn)發(fā)。
5、配置瀏覽器插件或工具
有些瀏覽器插件或工具(如Postman、curl等)可以繞過瀏覽器的同源策略限制,直接發(fā)送跨域請求。但這種方法主要用于開發(fā)和測試階段,不建議在生產(chǎn)環(huán)境中使用。
補(bǔ)充:關(guān)于Spring Security的CSRF
關(guān)于Spring Security的CSRF
一、CSRF是什么
CSRF(Cross-Site Request Forgery,跨站請求偽造)是一種常見的網(wǎng)絡(luò)攻擊方式,攻擊者通過偽裝用戶的請求,利用用戶在其他網(wǎng)站上已經(jīng)登錄的身份權(quán)限來執(zhí)行惡意操作。為了防止 CSRF 攻擊,Spring Security 提供了內(nèi)置的 CSRF 保護(hù)功能。
二、Spring Security中的CSRF保護(hù)
在Spring框架中,如果引入了Spring Security依賴,除了會默認(rèn)開啟身份驗(yàn)證,同時還會開啟CSRF保護(hù)功能。
啟用此功能可確保,在會更改狀態(tài)的HTTP請求上,如果沒有帶上有效的X-CSRFToken,則會出現(xiàn) 403 錯誤。
HTTP狀態(tài)碼403表示禁止訪問,即服務(wù)器理解請求客戶端的請求,但是拒絕執(zhí)行這個請求。這通常是由于客戶端沒有足夠的權(quán)限訪問該資源所導(dǎo)致的。
可以認(rèn)為安全的方法都是只讀的方法(GET, HEAD, OPTIONS),這些類型的請求不會改變資源狀態(tài),被認(rèn)為是冪等的,即調(diào)用相同的URL多次得到的結(jié)果不變。
DELETE方法的語義表示刪除服務(wù)器上的一個資源,第一次刪除成功后該資源就不存在了,資源狀態(tài)改變了,所以DELETE方法不具備安全特性。然而HTTP協(xié)議規(guī)定DELETE方法是冪等的,每次刪除該資源都要返回狀態(tài)碼200 OK,服務(wù)器端要實(shí)現(xiàn)冪等的DELETE方法,必須記錄所有已刪除資源的元數(shù)據(jù)(Metadata),否則,第二次刪除后返回的響應(yīng)碼就會類似404 Not Found了。
PUT和POST方法語義中都有修改資源狀態(tài)的意思,因此都不是安全的。但是PUT方法是冪等的,POST方法不是冪等的,這么設(shè)計的理由是:
HTTP協(xié)議規(guī)定,POST方法修改資源狀態(tài)時,URL指示的是該資源的父級資源,待修改資源的ID信息在請求體中攜帶。而PUT方法修改資源狀態(tài)時,URL直接指示待修改資源。因此,同樣是創(chuàng)建資源,重復(fù)提交POST請求可能產(chǎn)生兩個不同的資源,而重復(fù)提交PUT請求只會對其URL中指定的資源起作用,也就是只會創(chuàng)建一個資源。
因此,當(dāng)Spring Security使用默認(rèn)配置,或手動開啟CSRF保護(hù)功能時,我們會發(fā)現(xiàn),GET請求是正常的,但是POST請求會報403錯誤。
三、處理方法
處理方法一:跳過安全檢查(不推薦,因?yàn)檫@個一般是用于靜態(tài)資源等)
@Bean //(Spring Security6.2.4寫法) public WebSecurityCustomizer webSecurityCustomizer() { // 匹配的路徑,直接跳過安全檢查 return (web) -> web.ignoring().requestMatchers("/static/**"); }
處理方法二:關(guān)閉CSRF保護(hù)功能
@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()); //只對匹配路徑關(guān)閉CSRF //.csrf((csrf)->csrf.ignoringRequestMatchers("/路徑")); return http.build(); }
處理方法三:正常在請求時帶上X-CSRFToken
到此這篇關(guān)于關(guān)于Spring Security的CORS的文章就介紹到這了,更多相關(guān)Spring Security的CORS內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Java把文本內(nèi)容轉(zhuǎn)換成網(wǎng)頁的實(shí)現(xiàn)方法分享
這篇文章主要介紹了使用Java把文本內(nèi)容轉(zhuǎn)換成網(wǎng)頁的實(shí)現(xiàn)方法分享,利用到了Java中的文件io包,需要的朋友可以參考下2015-11-11IntelliJ IDEA版Postman強(qiáng)大功能介紹
這篇文章主要為大家介紹了IDEA版Postman的強(qiáng)大功能介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06java虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)分析
這篇文章主要介紹了java虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)分析,具有一定參考價值,需要的朋友可以了解下。2017-11-11Springboot PostMapping無法獲取數(shù)據(jù)問題及解決
這篇文章主要介紹了Springboot PostMapping無法獲取數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05Java中遇到的For?input?string問題解決辦法
這篇文章主要給大家介紹了關(guān)于Java中遇到的For?input?string問題的解決辦法,如果出現(xiàn)這樣的異常報錯,是指的數(shù)據(jù)轉(zhuǎn)換時出錯,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11springboot使用GuavaCache做簡單緩存處理的方法
這篇文章主要介紹了springboot使用GuavaCache做簡單緩存處理的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片
這篇文章主要為大家詳細(xì)介紹了Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05