運(yùn)行環(huán)境
操作系統(tǒng):Windows 10 ;
開發(fā)工具:IDEA-2019.3;
Web服務(wù)器:Tomcat 9.0.24;
JDK版本: jdk 1.8.0_221;
Spring boot版本:2.0.9.RELEASE
開始
想起最早搭建spring boot框架的時(shí)候,就遇到了跨域的問題。當(dāng)時(shí),也嘗試去解決,雖然,最終解決了,但是,總是感覺稀里糊涂的。
這次看了公眾號(hào)的文章《Cors跨域(一):深入理解跨域請(qǐng)求概念及其根因》,又燃起了我嘗試重新梳理跨域問題的想法。
若想實(shí)現(xiàn)Cors機(jī)制的跨域請(qǐng)求,是需要瀏覽器和服務(wù)器同時(shí)支持的。關(guān)于瀏覽器對(duì)CORS的支持情況:現(xiàn)在都2021年了,so可以認(rèn)為100%的瀏覽器都是支持的,再加上CORS的整個(gè)過程都由瀏覽器自動(dòng)完成,前端無需做任何設(shè)置,所以前端工程師的ajax原來怎么用現(xiàn)在還是怎么用,它對(duì)前段開發(fā)人員是完全透明的。
我摘錄了文章的一段話,從上面可以看出來,B/S架構(gòu)下的項(xiàng)目,前端我們不做任何修改,我們主要解決的后端的問題。而后端的處理的目的也是為了讓前端知道后端是如何允許跨域請(qǐng)求的。
方法1. 使用Filter過濾器解決
這里不依賴spring boot。主要思路就是開發(fā)一個(gè)Filter,并讓Spring容器去掃描注冊(cè)。
@Component
//filter雖然是servlet的三大組件之一,但是也是需要顯式的注冊(cè)在xml中的,后續(xù)servlet3.0后支持注解配置,使用@componet注解,
//讓spring 去掃描filter,并注冊(cè)使用
public class CORSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "content-type,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
方法2:配置FilterRegistrationBean
注意FilterRegistrationBean是spring boot框架提供的一個(gè)類。這個(gè)類為我們注冊(cè)filter提供了便捷。我們要注冊(cè)的也是spring-web內(nèi)部提供的CorsFilter。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
//跨域相關(guān)配置
Map<String, CorsConfiguration> corsConfigs = new LinkedHashMap<>();
corsConfigs.put("*", new CorsConfiguration().applyPermitDefaultValues());
corsConfigs.put("/**", new CorsConfiguration().applyPermitDefaultValues());
//組裝跨域ConfigurationSource
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.setCorsConfigurations(corsConfigs);
//組裝CorsFilter
CorsFilter corsFilter = new CorsFilter(configSource);
//注冊(cè)filter
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(corsFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setName("corsFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
方法3:使用@CrossOrigin
使用@CrossOrigin,能夠更加細(xì)粒度的控制跨域請(qǐng)求。
@CrossOrigin
@RequestMapping("/home")
public String home(Model model) {
return "home";
}
方法4:使用WebMvcConfigurer方式全局配置
使用spring mvc 提供的接口WebMvcConfigurer配置跨域。
@Configuration
public class WebSecurityConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
//該字段是必須的,用來列出瀏覽器的CORS請(qǐng)求會(huì)用到哪些HTTP方法,
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
//該字段是一個(gè)逗號(hào)分隔的字符串,指定瀏覽器CORS請(qǐng)求會(huì)額外發(fā)送的頭信息字段,上例是X-Custom-Header。
.allowedHeaders("Access-Control-Allow-Origin","token","secret")
//CORS請(qǐng)求時(shí),XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到6個(gè)基本字段。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。
//.exposedHeaders("token","secret")
.allowCredentials(true)//它的值是一個(gè)布爾值,表示是否允許發(fā)送Cookie。默認(rèn)情況下,Cookie不包括在CORS請(qǐng)求之中
//該字段可選,用來指定本次預(yù)檢請(qǐng)求的有效期,單位為秒
.maxAge(3600);
}
}
上面4中方法,第一種用了servlet的知識(shí),第二種用了spring boot的知識(shí),第三種用了spring框架的細(xì)粒度控制注解,第四種是spring框架提供專門跨域解決方案。
第一種最好理解,第四種是最優(yōu)雅解決方案。
總結(jié)
在解決這個(gè)問題的時(shí)候,由于涉及的知識(shí)塊很多。會(huì)遇到的宏觀問題有:
- 1.你不知道這是屬于spring框架的,spring boot的,還是servlet的。
- 2.這是幾個(gè)知識(shí)塊組合才能解決的。
- 3.可以從任意一個(gè)知識(shí)塊的角度出發(fā)都可以解決這個(gè)問題,條條大路通羅馬。
你在解決這個(gè)問題之前,這3個(gè)問題始終都會(huì)纏繞在一起,讓人禿頭。等回頭看,這3個(gè)問題都有三個(gè)正確的答案。但是,我們又浪費(fèi)了很多的時(shí)間。
我從別人身上學(xué)到一個(gè)優(yōu)點(diǎn),就是,以解決問題為導(dǎo)向。最重要的,是先要解決問題,不要管解決的方案是不是很啰嗦,是不是不優(yōu)雅。只有解決了問題,才能回過頭剖析問題,優(yōu)化方案。