在使用SpringCloud实现微服务时,常常会碰到前端页面访问多个二级域名的状况,跨域是首先要解决的问题。前端
解决这个问题,能够从两方面入手,一种方案是在微服务各自的业务模块中实现,即在SpringBoot层实现,另一种方案就是在Gateway层实现。spring
首先讲一下在SpringBoot层实现的三种方案。json
这种方式适合只有一两个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; } }
若是有大量的rest接口的时候,显然第一种方案已经不适合了,工做量大,也容易出错,那就经过全局配置的方式,容许SpringBoot端全部的rest接口都支持跨域访问,这个时候就须要考虑安全性的问题。缓存
代码以下:安全
@Configuration服务器
public class MyConfiguration {cookie
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedMethods("GET"); } }; }
}app
这种方案的使用场景跟第二种方案相似,只不过换成使用Filter的方式实现。
在spring boot的主类中,增长一个CorsFiltercors
/** * attention:简单跨域就是GET,HEAD和POST请求,可是POST请求 的"Content-Type"只能是application/x-www-form-urlencoded, multipart/form-data 或 text/plain * 反之,就是非简单跨域,此跨域有一个预检机制,说直白点,就是会发两次请求,一次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端,对于有多个微服务模块的状况下,就大大减小了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的文档你就会发现,原来CorsFilter早已经在Gateway里了,不须要本身写代码实现,并且更灵活,修改配置文件便可,结合配置中心使用,能够实现动态修改。
application.yml.
spring:
cloud:
gateway: globalcors: corsConfigurations: '[/**]': allowedOrigins: "docs.spring.io" allowedMethods: - GET
以上五种跨域方案讲完了,你用的哪种呢?仍是在本身写代码实现吗?