Spring boot 解決 CORS跨域問題

運(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)化方案。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容