头图

引起跨域的原因

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,
则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

跨域的最佳实践
生产环境中,最好是在服务端的入口出解决跨越问题,比如通过nginx或者API网关解决跨域问题,
这样我们的每个微服务就不需要关注跨域的问题。因为跨域如果解决多次,还会出现错误。导致客户端不能正常访问。
那既然说跨域问题应该交给请求的入口解决,那么我们的SpringBoot项目是否还需要解决跨域问题呢?答案是需要,因为在日常联调的时候,我们很可能使用自己的机器进行联调,客户端直接调用,不会走微服务的网关
而且,我们为微服务提供了可以解决跨域的能力,并用开关的形式选配跨域,这样即使API网关没有解决跨域,客户端也能正确访问我们的微服务。
SpringBoot解决跨域
SpringBoot提供了CorsFilter工具类,我们通过配置CorsConfiguration配置跨域,即可解决跨域问题。

一个最粗暴的跨域配置
全部使用默认的跨域配置,默认的跨域配置意味着所有的请求都会被接受,任何请求都不会跨域,但是这种是很危险的行为,再生产环境中谨慎使用。

java 代码解读复制代码 @Bean

public CorsFilter corsFilter() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
    urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
    return new CorsFilter(urlBasedCorsConfigurationSource);
}

通过手动传入跨域配置规则,定制解决跨域
比如,这里只允许请求为GET和POST请求,并且请求的header中只能有token选项,否则会跨域,针对这种场景我们可以使用如下配置

java 代码解读复制代码 @Bean

public CorsFilter corsFilter() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    
    corsConfiguration.setAllowedHeaders(Collections.singletonList("token"));
    corsConfiguration.setAllowedMethods(Arrays.asList("GET","POST"));
    
    UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
    urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
    return new CorsFilter(urlBasedCorsConfigurationSource);
}


解决SpringBoot的配置文件,避免硬编码问题
我们可以使用SpringBoot的配置文件的能力,将跨域信息配置到配置文件中,避免程序的硬编码, 并借助Spring的ConditionalOnProperty为跨域装上开关
这样可以很灵活的针对不同场景进行跨域配置,而且这种形式可以完全独立出来通过SpringBoot的自动/手动装配成一个独立的组件
3.1 跨域配置类
java 代码解读复制代码 @Data

 @Component
 @ConfigurationProperties(prefix = "example.web.cors")
 public class CorsProperties {
 /**
 * 是否开启跨域解决
 */
 private Boolean enable;
 /**
 * allowedOrigins 默认是*
 */
 private List<String> allowedOrigins;
 /**
 * allowedMethods 默认是*
 * 例如:{GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE}
 */
 private List<String> allowedMethods;
 /**
 * allowedHeaders 默认是*
 */
 private List<String> allowedHeaders;
 /**
 * exposedHeaders 默认是*
 */
 private List<String> exposedHeaders;
 /**
 * allowCredentials 默认是 true
 */
 private Boolean allowCredentials = true;
 /**
 * maxAge
 */
 private Long maxAge;
 
 }

3.2 通过配置构造跨域对象
java 代码解读复制代码 @Bean
@ConditionalOnProperty(

     prefix = "example.web.cors",
     name = {"enable"},
     havingValue = "true"

)
@ConditionalOnMissingBean(value = CorsFilter.class)
public CorsFilter corsFilter(CorsProperties corsProperties) {

 log.info("\ncors enabled.");
 CorsConfiguration corsConfiguration = generatorCorsConfiguration(corsProperties);
 final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
 urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
 return new CorsFilter(urlBasedCorsConfigurationSource);

}

/**

  • 如果没有配置,使用默认的解决方案
    */

private CorsConfiguration generatorCorsConfiguration(CorsProperties corsProperties) {

 final CorsConfiguration corsConfiguration = new CorsConfiguration();

 List<String> allowedOrigins = corsProperties.getAllowedOrigins();
 if (Objects.isNull(allowedOrigins)) {
     corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL);
 } else {
     corsConfiguration.setAllowedOrigins(allowedOrigins);
 }

 List<String> allowedHeaders = corsProperties.getAllowedHeaders();
 if (Objects.isNull(allowedHeaders)) {
     corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
 } else {
     corsConfiguration.setAllowedHeaders(allowedHeaders);
 }

 List<String> allowedMethods = corsProperties.getAllowedMethods();
 if (Objects.isNull(allowedMethods)) {
     corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
 } else {
     corsConfiguration.setAllowedMethods(allowedMethods);
 }

 Boolean allowCredentials = corsProperties.getAllowCredentials();
 corsConfiguration.setAllowCredentials(allowCredentials);
 corsConfiguration.setMaxAge(corsProperties.getMaxAge());
 return corsConfiguration;

}

3.3 配置文件演示
yaml 代码解读复制代码example:
web:

 cors:
  # 跨域开关
  enable: true
   allowed-headers:
   - token
   allowed-methods:
   - GET
   - POST

转载来源:https://juejin.cn/post/6999996325907398686


运维社
9 声望4 粉丝