Spring Cloud配置跨域访问的五种方案
2020-07-01 16:44:47 【

在使用SpringCloud实现微服务时,经常会碰到前端页面访问多个二级域名的情况,跨域是首先要解决的问题。


解决这个问题,可以从两方面入手,一种方案是在微服务各自的业务模块中实现,即在SpringBoot层实现,另外一种方案就是在Gateway层实现。


首先讲一下在SpringBoot层实现的三种方案。


一,在Controller上添加@CrossOrigin注解

这种方式适合只有一两个rest接口需要跨域或者没有网关的情况下,这种处理方式就非常简单,适合在原来基代码基础上修改,影响比较小。


/* 注解方式 */

   @CrossOrigin

   @RestController

   public class HandlerScanController {

       @CrossOrigin(allowCredentials = "true", allowedHeaders = "*", methods = {RequestMethod.GET,

               RequestMethod.POST, RequestMethod.DELETE, RequestMethod.OPTIONS,

               RequestMethod.HEAD,

               RequestMethod.PUT,

               RequestMethod.PATCH},

               origins = "*")

       @PostMapping("/confirm")

       public Response handler(@RequestBody Request json) {

           return (null);

       }

   }

二,增加WebMvcConfigurer全局配置

如果有大量的rest接口的时候,显然第一种方案已经不适合了,工作量大,也容易出错,那就通过全局配置的方式,允许SpringBoot端所有的rest接口都支持跨域访问,这个时候就需要考虑安全性的问题。


代码如下:


@Configuration

public class MyConfiguration {

   @Bean

   public WebMvcConfigurer corsConfigurer() {

       return new WebMvcConfigurerAdapter() {

           @Override

           public void addCorsMappings(CorsRegistry registry) {

               registry.addMapping("/**").allowCredentials(true).allowedMethods("GET");

           }

       };

   }

}

三,结合Filter使用

这种方案的使用场景跟第二种方案类似,只不过换成使用Filter的方式实现。

在spring boot的主类中,增加一个CorsFilter


/**

    * attention:简单跨域就是GET,HEAD和POST请求,但是POST请求  的"Content-Type"只能是application/x-www-form-urlencoded, multipart/form-data 或 text/plain

    * <p>

    * 反之,就是非简单跨域,此跨域有一个预检机制,说直白点,就是会发两次请求,一次OPTIONS请求,一次真正的请求

    */

   @Bean

   public CorsFilter corsFilter() {

       final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

       final CorsConfiguration config = new CorsConfiguration();

       config.setAllowCredentials(true); // 允许cookies跨域

       config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin

       config.addAllowedHeader("*");// #允许访问的头信息,*表示全部

       config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了

       config.addAllowedMethod("OPTIONS");// 允许提交请求的方法,*表示全部允许

       config.addAllowedMethod("HEAD");

       config.addAllowedMethod("GET");// 允许Get的请求方法

       config.addAllowedMethod("PUT");

       config.addAllowedMethod("POST");

       config.addAllowedMethod("DELETE");

       config.addAllowedMethod("PATCH");

       source.registerCorsConfiguration("/**", config);

       return new CorsFilter(source);

   }

以上这种方案如果微服务多的话,需要在每个服务的主类上都加上这么段代码,增加了维护量。


以上三种方案都是在SpringBoot的基础上实现的解决方案,在模块较多或者接口较多的情况下不易维护。


既然SpringCloud自带Gateway,下面就讲讲使用Gateway的跨域解决方案。


四,在Gateway端增加CorsFilter拦截器

这种方案跟方案三有些类似,只不过是放到了Gateway端,对于有多个微服务模块的情况下,就大大减少了SpringBoot模块端的代码量,让各个模块更集中精力做业务逻辑实现。这个方案只需要在Gateway里添加Filter代码类即可。


public class CorsWebFilter implements WebFilter {

   private static final String ALL = "*";

   private static final String MAX_AGE = "18000L";

   @Override

   public Mono<Void> filter(ServerWebExchange ctx, WebFilterChain chain) {

       ServerHttpRequest request = ctx.getRequest();

       String path = request.getPath().value();

       ServerHttpResponse response = ctx.getResponse();

       if ("/favicon.ico".equals(path)) {

           response.setStatusCode(HttpStatus.OK);

           return Mono.empty();

       }

       if (!CorsUtils.isCorsRequest(request)) {

           return chain.filter(ctx);

       }

       HttpHeaders requestHeaders = request.getHeaders();

       HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();

       HttpHeaders headers = response.getHeaders();

       headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());

       headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());

       if (requestMethod != null) {

           headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());

       }

       headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");

       headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);

       headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);

       if (request.getMethod() == HttpMethod.OPTIONS) {

           response.setStatusCode(HttpStatus.OK);

           return Mono.empty();

       }

       return chain.filter(ctx);

   }

}

五,修改Gateway配置文件

在仔细阅读过Gateway的文档你就会发现,原来CorsFilter早已经在Gateway里了,不需要自己写代码实现,而且更灵活,修改配置文件即可,结合配置中心使用,可以实现动态修改。


application.yml


spring:

   cloud:

       gateway:      

         globalcors:        

           corsConfigurations:

             '[/**]':

               allowedOrigins: "http://domain.com"

               allowedMethods:

               - GET

               - POST



】【打印关闭】 【返回顶部
上一篇k8s之PodIP、ClusterIP和External.. 下一篇游戏服务器架构演化史