Springboot处理CORS跨域请求的三种方法

发布于 2021-06-15  81 次阅读


一.前言

Springboot跨域问题,是当前主流web开发人员都绕不开的难题。但我们首先要明确以下几点

  • 跨域只存在于浏览器端,不存在于安卓/ios/Node.js/python/ java等其它环境
  • 跨域请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
  • 之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。换句话说,浏览器安全的基石是同源策略。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

先给出一个熟悉的报错信息,让你找到家的感觉~

img

Access to XMLHttpRequest at 'http://192.168.1.1:8080/app/easypoi/importExcelFile' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

二.什么是CROS?

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。

CORS Header

  • Access-Control-Allow-Origin: http://www.xxx.com
  • Access-Control-Max-Age:86400
  • Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, DELETE
  • Access-Control-Allow-Headers: content-type
  • Access-Control-Allow-Credentials: true

含义解释:

CORS Header属性解释
Access-Control-Allow-Origin允许http://www.xxx.com域(自行设置,这里只做示例)发起跨域请求
Access-Control-Max-Age设置在86400秒不需要再发送预校验请求
Access-Control-Allow-Methods设置允许跨域请求的方法
Access-Control-Allow-Headers允许跨域请求包含content-type
Access-Control-Allow-Credentials设置允许Cookie

三.SpringBoot跨域请求处理方式

方法一、直接采用SpringBoot的注解@CrossOrigin(也支持SpringMVC)

简单粗暴的方式,Controller层在需要跨域的类或者方法上加上该注解即可

@RestController
@CrossOrigin
@RequestMapping("/situation")
public class SituationController extends PublicUtilController {
 
 @Autowired
 private SituationService situationService;
 // log日志信息
 private static Logger LOGGER = Logger.getLogger(SituationController.class);
}

方法二、处理跨域请求的Configuration

增加一个配置类,CorsConfig.java。继承WebMvcConfigurerAdapter或者实现WebMvcConfigurer接口,其他都不用管,项目启动时,会自动读取配置。

@Configuration
public class CorsConfig implements WebMvcConfigurer {
   @Override
   public void addCorsMappings(CorsRegistry registry) {
       registry.addMapping("/**").
               allowedOriginPatterns("*"). //允许跨域的域名,可以用*表示允许任何域名使用
//               allowedOrigins("*").   //在Springboot2.4对应Spring5.3后在设置allowCredentials(true)的基础上不能直接使用通配符设置allowedOrigins,而是需要指定特定的URL。如果需要设置通配符,需要通过allowedOriginPatterns指定
               allowedMethods("GET", "POST", "DELETE", "PUT") . //允许任何方法(post、get等)
               allowedHeaders("*"). //允许任何请求头
               allowCredentials(true). //带上cookie信息
               exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果


  }


}

方法三、采用过滤器(filter)的方式

同方法二加配置类,增加一个CORSFilter 类,并实现Filter接口即可,其他都不用管,接口调用时,会过滤跨域的拦截。

package com.shiyun.filter;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CoresFilter implements Filter {
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {

  }

   @Override
   public void destroy() {

  }

   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       HttpServletResponse response = (HttpServletResponse) servletResponse;
       response.setHeader("Access-Control-Allow-Origin", "*");
       response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");
       response.setHeader("Access-Control-Max-Age", "3600");
       response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
       filterChain.doFilter(servletRequest, servletResponse);
  }
}

或者使用CorsConfiguration和UrlBasedCorsConfigurationSource

@Configuration
public class CorsConfig1 {
   /**
    * @Description :
    * @Date 11:18 2021/2/21 0021
    * @Param * @param :
    * @return org.springframework.web.cors.CorsConfiguration
    **/
   private CorsConfiguration buildConfig() {
       CorsConfiguration corsConfiguration = new CorsConfiguration();
       corsConfiguration.setAllowCredentials(true);
       //允许跨域的域名,可以用*表示允许任何域名使用, 在Springboot2.4对应Spring5.3后在设置allowCredentials(true)的基础上不能直接使用通配符设置allowedOrigins,而是需要指定特定的URL。如果需要设置通配符,需要通过allowedOriginPatterns指定
       //       corsConfiguration.addAllowedOrigin("*");
       corsConfiguration.addAllowedOriginPattern("*");
       corsConfiguration.addAllowedHeader("*");
       corsConfiguration.addAllowedMethod("*");
       return corsConfiguration;
  }

   /**
    * @Description :
    * @Date 11:19 2021/2/21 0021
    * @Param * @param :
    * @return org.springframework.web.filter.CorsFilter
    **/
   @Bean
   public CorsFilter corsFilter() {
       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
       source.registerCorsConfiguration("/**", buildConfig());
       return new CorsFilter(source);
  }

}

过滤器设置执行顺序:



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;

/**
* Classname:CorsFilterConfig
*
* @description:解决跨域请求问题
* @author: 陌意随影
* @Date: 2021-05-30 01:54
* @Version: 1.0
**/
@Configuration
public class CorsFilterConfig {
   /**
    * @Description :跨域访问过滤器,设置执行顺序
    * @Date 19:55 2021/6/15 0015
    * @return org.springframework.boot.web.servlet.FilterRegistrationBean<org.springframework.web.filter.CorsFilter>
    **/
  @Bean
   public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean(){
       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
       CorsConfiguration config = new CorsConfiguration();
      config.addAllowedOriginPattern("*");
      config.addAllowedHeader("*");
      config.addAllowedMethod("*");
      config.setAllowCredentials(true);
       source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
       FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
       //设置执行顺序,数字越小越先执行
       bean.setOrder(0);
       return bean;
  }
}

繁华落尽,雪花漫天飞舞。