这里记录一次使用SpringBoot项目配置CORS跨域的写法和中间遇到的问题。
使用SpringBoot配置CORS跨域的方式有三种,下边分别介绍下。

一、方式一:使用@CrossOrigin注解

如果想要对某一接口配置 CORS,可以在方法上添加 @CrossOrigin 注解:

@CrossOrigin(origins = {"http://localhost:8080", "null"})
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String greetings() {
    return "test";
}

如果想对一系列接口添加 CORS 配置,可以在类上添加注解,对该类声明所有接口都有效:

@CrossOrigin(origins = {"http://localhost:8080", "null"})
@RestController
@SpringBootApplication
public class SpringBootCorsTestApplication {
    ......
}

二、方式二:添加全局配置,需要加一个配置类

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
//                .allowedOrigins("*")
                .allowedOriginPatterns("*")// 设置允许跨域请求的域名
                .allowedHeaders("*"// 设置允许的请求头
                .allowCredentials(true)// 是否允许证书
                .allowedMethods("*")// 允许的方法
                .maxAge(3600);// 跨域允许时间
    }
}

注意,当我项目里使用的springboot的版本是2.1.2.RELEASE时,使用allowedOrigins(“*”)没有问题,但是当版本改为2.4.4后会提示报错如下:

When allowCredentials is true, allowedOrigins cannot contain the special value “*” “since that cannot be set on the “Access-Control-Allow-Origin” response header. To allow credentials to a set of origins, list them explicitly or consider using"allowedOriginPatterns” instead.

翻译:当allowCredentials为true时,allowedOrigins不能包含特殊值"* “,因为它不能在” Access-Control-Allow-Origin "响应头中设置。要允许一组起源的凭证,明确地列出它们,或者考虑使用“allowedOriginPatterns”代替。

所以将.allowedOrigins("*")换成.allowedOriginPatterns("*")后解决。

三、方式三:使用Filter过滤器进行配置

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);	     
    // config.addAllowedOrigin("*");
    config.addAllowedOriginPattern("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
    return new CorsFilter(source);
}

四、原理剖析

无论是通过哪种方式配置 CORS,其实都是在构造 CorsConfiguration。 一个 CORS 配置用一个 CorsConfiguration类来表示,它的定义如下:

public class CorsConfiguration {
    private List<String> allowedOrigins;
    private List<String> allowedMethods;
    private List<String> allowedHeaders;
    private List<String> exposedHeaders;
    private Boolean allowCredentials;
    private Long maxAge;
}

Spring 中对 CORS 规则的校验,都是通过委托给 DefaultCorsProcessor 实现的。
DefaultCorsProcessor 处理过程如下:

  1. 判断依据是 Header 中是否包含 Origin。如果包含则说明为 CORS请求,转到 2;否则,说明不是 CORS 请求,不作任何处理。
  2. 判断 response 的 Header 是否已经包含 Access-Control-Allow-Origin,如果包含,证明已经被处理过了, 转到 3,否则不再处理。
  3. 判断是否同源,如果是则转交给负责该请求的类处理。
  4. 是否配置了 CORS 规则,如果没有配置,且是预检请求,则拒绝该请求,如果没有配置,且不是预检请求,则交给负责该请求的类处理。如果配置了,则对该请求进行校验。

校验就是根据 CorsConfiguration 这个类的配置进行判断:

  1. 判断 origin 是否合法
  2. 判断 method 是否合法
  3. 判断 header是否合法
  4. 如果全部合法,则在 response header中添加响应的字段,并交给负责该请求的类处理,如果不合法,则拒绝该请求。

五、遇到的问题

刚开始我的拦截器是通过实现WebMvcConfigurer接口,然后重写addCorsMappings(CorsRegistry registry)方法添加跨域设置的,也就是上边的方式二,但是我项目里有自定义的拦截器,在所有请求前加了登陆状态的判断(会获取header里的自定义token进行判断)。这个时候就会出现问题,在获取自定义header时为null,前端一直报跨域问题:

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

原因是请求经过的先后顺序问题,请求会先进入到自定义拦截器中,而不是进入Mapping映射中,所以返回的头信息中并没有配置的跨域信息,浏览器就会报跨域异常。所以改为使用Filter的方式配置CORS跨域,解决问题。

参考链接:
链接1
链接2

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐