SpringSecurity中的跨域問題處理方案
跨域問題是實際應用開發(fā)中一個非常常見的需求,在Spring框架中對于跨域問題的處理方案有好幾種,引入了Spring Security之后,跨域問題的處理方案又增加了。
1.什么是CORS
CORS(Cross-Origin Resource Sharing)是由W3C制定的一種跨域資源共享技術標準,其目的就是為了解決前端的跨域請求。在JavaEE開發(fā)中,最常見的前端跨域請求解決方案是早起的JSONP,但是JSONP只支持GET請求,這是一個很大的缺陷,而CORS則支持多種HTTP請求方法,也是目前主流的跨域解決方案。
CORS中新增了一組HTTP請求頭字段,通過這些字段,服務器高爐瀏覽器,哪些網站通過瀏覽器有權限訪問哪些資源。同時規(guī)定,對那些可能修改服務器數(shù)據(jù)的HTTP請求方法(如GET以外的HTTP請求等),瀏覽器必須首先使用OPTIONS方法發(fā)起一個預檢請求(prenightst),預檢請求的目的是查看服務端是否支持即將發(fā)起的跨域請求,如果服務端允許,才發(fā)送實際的HTTP請求。在預檢請求的返回中,服務端也可以通知客戶端,是否需要攜帶身份憑證(如Cookies、HTTP認證信息等)。
CORS: 同源/同域 = 協(xié)議 + 主機 + 端口
2.簡單請求
GET請求為例,如果需要發(fā)起一個跨域請求,則請求頭如下:
Host: localhost:8080 Origin: http://localhost:8081 Referer: http://localhost:8081/index.html
如果五段支持該跨域請求,那么返回的響應頭中將包含如下字段:
Access-Control-Allow-Origin: http://localhost:8081
Access-Control-Allow-Origin字段用來告訴瀏覽器可以訪問該資源的域,當瀏覽器收到這樣的響應頭信息之后,提取出Access-Control-Allow-Origin字段中的值,發(fā)現(xiàn)該值包含當前頁面所在的域,就知道這個跨域是被允許的,因此就不再對前端的跨域請求進行限制。這屬于簡單請求,即不需要進行預檢請求的跨域。
3.非簡單請求
對于一些非簡單請求,會首先發(fā)送一個預檢請求。預檢請求類似下面這樣:
OPTIONS /put HTTP/1.1 Host: localhost:8080 Connection: keep-alive Accept: */* Access-Control-Request-Method: PUT Origin: http://localhost:8081 Referer: http://localhost:8081/index.html
請求方法是OPTIONS,請求頭Origin就鈣素服務端當前頁面所在域,請求頭Access-Control-Request-Methods告訴服務器端即將發(fā)起的跨域請求所使用的方法。服務端對此進行判斷,如果允許即將發(fā)起的跨域請求,則會給出如下響應:
HTTP/1.1 200 Access-Control-Allow-Origin:http://localhost: 8081 Access-Control-Request-Methods: PUT Access-Control-Max-Age: 3600
Access-Control-Allow-Methods字段表示允許的跨域方法:Access-Control-Max-Age字段表示預檢請求的有效期,單位為秒,在有效期內如果發(fā)起該跨域請求,則不用再次發(fā)起預檢請求。預檢請求結束后,接下來就會發(fā)起一個真正的跨域請求,跨域請求和前面的簡單請求跨域步驟類似。
4.Spring跨域解決方案
4.1.@CrossOrigin
Spring中第一種處理跨域的方式是通過@CrossOrigin注解來標記支持跨域,該注解可以添加在方法上,也可以添加在Controller上。當添加在Controller上時,表示Controller中的所有接口都支持跨域,具體配置如下:
@RestController
public class IndexController {
@GetMapping("/hello")
@CrossOrigin(origins = "http://localhost:8081")
public String hello(){
System.out.println("hello ok");
return "hello ok";
}
}@CrossOrigin注解各屬性含義如下:
- allowCredentials:瀏覽器是否應當發(fā)送憑證信息,如Cookie。
- allowedHeaders:請求被允許的請求頭字段,*表示所有字段。
- exposedHeaders:哪些響應頭可以作為相應的一部分暴露出來。
注意,這里只可以一一列舉,通配符 * 在這里是無效的。
- maxAge:預檢請求的有效期,有效期內不必再次發(fā)送預檢請求,默認是1800秒
- methods:允許的請求方法,*表示允許的所有方法。
- origins:允許的域,*表示允許所有域。
4.2.addCrossMapping自定義mvc配置
@CrossOrigin注解需要添加在不同的Controller上。所以還有一種全局配置方法,就是通過重寫WebMvcConfigurerComposite#addCorsMappings方法來實現(xiàn),具體配置如下:
//自定義mvc配置類
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//用來全局處理跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //處理的請求地址
.allowedMethods("*")
.allowedOrigins("*")
.allowedHeaders("*")
.allowCredentials(false)
.exposedHeaders("")
.maxAge(3600);
}
}4.3.CorsFilter
CorsFilter是Spring Web中提供的一個處理跨域的過濾器,開發(fā)者也可以通過該過濾器處理跨域。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
/**
* @author zhangKaiTao
* @date 2022/7/24 10:30
*/
//自定義mvc配置類
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
FilterRegistrationBean<CorsFilter> corsFilter(){
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",corsConfiguration);
registrationBean.setFilter(new CorsFilter(source));
registrationBean.setOrder(-1);
return registrationBean;
}
}5.SpringSecurity跨域解決方案
當我們?yōu)轫椖刻砑恿薙pring Security依賴后,發(fā)現(xiàn)上面三種跨域方式有的失效了,有的則可以繼續(xù)使用,這是怎么回事?
通過@CrossOrigin注解或者重寫addCorsMappings方法配置跨域,統(tǒng)統(tǒng)失效了,通過CorsFilter配置的跨域,有沒有失效則要看過濾器的優(yōu)先級,如果過濾器優(yōu)先級高于SpringSecurity過濾器,即先于Spring Security過濾器執(zhí)行,則CorsFilter所配置的跨域處理依然有效;如果過濾器優(yōu)先級地域Spring Security過濾器,則CorsFilyer所配置的跨域處理就會失效。
以下是Filter、DispatcherServlet以及Interceptor執(zhí)行順序。

由于非簡單請求都要首先發(fā)送一個預檢請求(request),而預檢請求并不會攜帶認證信息,所以預檢請求就有被Spring Security攔截的可能。因此通過@CrossOrigin注解或者重寫addCorsMappings方法配置跨域就會失效。如果使用CorsFilter配置的跨域,只要過濾器優(yōu)先級高于SpringSecurity過濾器就不會有問題,反之同樣會出現(xiàn)問題。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.cors().configurationSource(corsConfigurationSource())
.and()
.csrf().disable();//開啟csrf
}
CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",corsConfiguration);
return source;
}
}到此這篇關于SpringSecurity中的跨域問題處理方案的文章就介紹到這了,更多相關SpringSecurity跨域內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Boot集成ElasticSearch實現(xiàn)搜索引擎的示例
這篇文章主要介紹了Spring Boot集成ElasticSearch實現(xiàn)搜索引擎的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Springboot結合@validated優(yōu)化代碼驗證
這篇文章主要介紹了Springboot與@validated注解結合從而實現(xiàn)讓你的代碼驗證更清爽,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08
springboot2.2.2集成dubbo的實現(xiàn)方法
這篇文章主要介紹了springboot2.2.2集成dubbo的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-01-01
獲取JPEGImageEncoder和JPEGCode這兩個類的方法
下面小編就為大家?guī)硪黄@取JPEGImageEncoder和JPEGCode這兩個類的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
詳解Java中finally和return的執(zhí)行順序
try-catch-finally是一種針對程序運行時出錯的響應手段,對于一些可以預料到的出錯類型,在發(fā)生時對其進行報告和補救,這篇文章主要介紹了Java中finally和return的執(zhí)行順序,需要的朋友可以參考下2024-01-01
Java 對 Properties 文件的操作詳解及簡單實例
這篇文章主要介紹了Java 對 Properties 文件的操作詳解及簡單實例的相關資料,需要的朋友可以參考下2017-02-02

