文章目录
背景
在微服务架构中,通常一个系统会被拆分为多个微服务,面对这么多微服务客户端应该如何去调用呢?如果根据每个微服务的地址发起调用,存在如下问题:
- 客户端多次请求不同的微服务,会增加客户端代码和配置的复杂性,维护成本比价高
- 认证复杂,每个微服务可能存在不同的认证方式,客户端去调用,要去适配不同的认证
- 存在跨域的请求,调用链有一定的相对复杂性(防火墙 / 浏览器不友好的协议)
- 难以重构,随着项目的迭代,可能需要重新划分微服务
为了解决上面的问题,微服务引入了 API网关 的概念,API网关为微服务架构的系统提供简单、有效且统一的API路由管理,作为系统的统一入口,提供内部服务的路由中转
,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等
网关在架构中的位置
网关是在微服务访问的入口,对外是负载均衡 Nginx
Gateway简介
Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul。Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等
。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包
zuul 和 gateway
为什么选择 gateway?
- netflix 不靠谱,zuul2.0 一直跳票,迟迟不发布
- 一方面zuul 1.0 已经进入了维护阶段,而且GateWay 是spring cloud 团队研发的,值得信赖。 而且很多功能Zuul d都没用起来也非常的简单便捷。
- GateWay 是基于异步非阻塞模型上进行开发的, 性能方面都不需要担心,虽然 Netflix早就发布去了最新的 Zuul 2.x 但是 Spring Cloud 毛事没有整合计划。 而且Netflix 相关组件都宣布进行维护期;不知前景如何?
- 多方考虑Gateway 是很理想的网关选择
- springcloud gateway 具有如下特征
- 基于 Spring Framework5 . Project Reactor 和Spring Boot 2.0 进行构建。
- 动态路由:能够匹配任何请求属性;
- 可以路由指定 Predicate (断言) 和 Filter (过滤器)
- 集成 Hystrix 的断路器功能;
- 集成 Spring Cloud 服务发现功能
- 抑郁编写Predicate (断言) 和 Filter (过滤器)
- 请求限流共恩感
- 支持路径重写。
- springcloud gateway 和 zuul 的区别
在SpringCloud Finchley 正式版之前, Spring Cloud 推荐的网关是 Netflix 提供的 Zuul
1、Zuul 1.x 是基于阻塞 I/0 的 API Gateway
2、Zuul 1.x 基于 Servlet 2.5 使用阻塞架构他不支持任何常链接(如 WebSocket ) Zuul 的涉及模式和 Niginx 很像, 每次 I / 0 操作都是从工作线程种选择一个执行,请求线程被阻塞到工作线程完成,但是差别是 Nginx 使用的是 C++ 实现,Zuul 使用的是Java 实现,而JVM 本省会有第一次加载比较慢的情况,使得 Zuul 的性能相对较差
3.Zuul2.x 理想更为陷阱,想基于Netty 非阻塞和支持常谅解, 但是 SpringCloud 目前没有整合。 Zuul2.x 的性能较 Zuul1.x 有较大的提升。在性能方面,根据官方提供的基准测试, Spring Cloud Gateway 的 RPS(每秒请求数)是Zuul 的 1.6 倍
4.Spring Cloud Gateway 建立在 Spring Framework5、 Project Reactor 和Spring Boot 2之上,使用非阻塞 API。
5.Spring Cloud Gateway 还支持 websocket , 并且与 Spring 紧密集成拥有更好的开发体验。
zuul1.0 模型
Springcloud 中集成的Zuul 版本, 采用的是 Tomcat 容器,使用的是 传统的 Servlet IO 处理模型。
servlet 由 servlet container 进行生命周期管理。
- container 启动时,构造 servlet 对象并调用 servlet init() 进行初始化;
- container 运行时接受请求,并为每一个请求分配一个线程(一般从线程池中获取空闲线程,)然后调用service()
- container 关闭时调用 servlet destory ()销毁 servlet
上述模型的缺点:
servlet 是一个简单的网络 I/O 模型,当前请求进入servlet container 时,servlet container 就会为其绑定一个线程, 在并发不高的场景下,这种模型适用的。但是一旦在高并发(比如用jmeter压测), 线程数量就会上涨, 而线程资源代价时昂贵的(上下文切换,内存消耗大)严重影响请求的处理时间。在一些简单业务场景下, 不希望为每个 request分配一个线程,只需要1个或这几个线程就能应对极大的并发请求。这种场景下servlet 模型没有优势。
所以 zuul1.x 时基于 servelt 智商的一个阻塞式处理模型,即 spring 实现了, 处理
锁鱼哦??
request 请求的 servlet (DispatcherServlet ) 并由该 servlet 阻塞式处理。所以 ,springcloud zuul 无法摆脱 servlet 模型的弊端。
gateway 模型
webflux是什么?
传统 Web 框架, 比如说: struts2 , spring mvc 等都是基于 Servlet API 与 Servlet 容器之上运行的。
- 在Servlet3.1 之后有了异步非阻塞的支持。 而WebFlux 是一个典型的非阻塞的异步的框架,它的核心是基于 Reactor 的相关 API 实现的 , 相对于传统的Web 框架来说,他可以运行在诸如 Netty ,Undertow 支持 Servlet 3.1 的容器上。 非阻塞+ 函数式编程 (Spring 5 必须让你使用 java8)
Spring WebFlux
是Spring 5.0 引入的新的响应式框架,区别于 Spring MVC,它不需要依赖 Servlet API, 它完全是异步非阻塞的, 并且基于 Reactor 来实现响应式流规范
。
三大核心概念
- Route (路由)
路由是构建网关的基本模块, 它由ID, 目标 URI , 一系列的断言和过滤器组成,如果断言为true 则匹配该路由
- Predicate ( 断言)
Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。
如果断言为真,则说明请求的URL和配置的路由匹配
- Filter (过滤器)
SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理
总结
- web 请求,通过一些匹配条件,
定位到真正的服务节点,并在这个转发前后
,进行一些精细化的控制。 predicate 就是我们匹配的条件
:而 filter ,我们就理解为一个无所不能的拦截器,有了这两个元素,在加上目标 uri , 就是实现了一个具体的路由了。
Gateway 工作流程
- 客户端向 Spring Cloud Gateway 发出请求, 然后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler
- Handler 再通过制定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回
- 过滤器之间采用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或者之后 (“post”)执行业务逻辑
- Filter 在 “pre” 类型的过滤器可以做参数校验,权限校验、流量监控、日志输出、协议转换等。
- 在 “post” 类型的过滤器中可以做响应内容、响应头的修改、日志的输出,流量监控等有着非常重要的作用。
Spring Cloud Gateway实战
pom依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<!-- nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- loadbalance负载均衡 -->
<!-- springBoot3.x版本要单独引一下loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
注意:gateway会和spring-webmvc的依赖冲突,需要排除spring-webmvc`
网关后的微服务
假如现在有2个微服务,微服务名称分别为mall-order、mall-user
,且都注册到了Naocs上,在这两个微服务前加上网关。
yml配置文件:
spring:
application:
name: mall-gateway
#配置nacos注册中心地址
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一,建议配置成微服务名,这里没用微服务名是为了下面说明怎么调用的
uri: lb://mall-order #lb 整合负载均衡器loadbalancer
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
- id: user_route #路由ID,全局唯一,建议配置成微服务名
uri: lb://mall-user #lb 整合负载均衡器loadbalancer
predicates:
- Path=/user/** # 断言,路径相匹配的进行路由
以上yml中关于网关的配置,当请求路径是/order/**
时,在调用时,去Nacos中寻找uri
中lb
后的微服务名称,然后通过loadbalancer将lb
后的微服务名替换成ip:prot
+ 请求路径,调用真正的微服务(lb就是loadbalancer
)
predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
看一下代码中是如何判断请求是否符合断言的
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
synchronized (this.pathPatternParser) {
pathPatternParser.setMatchOptionalTrailingSeparator(
config.isMatchOptionalTrailingSeparator());
config.getPatterns().forEach(pattern -> {
PathPattern pathPattern = this.pathPatternParser.parse(pattern);
pathPatterns.add(pathPattern);
});
}
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 拿到请求的url
PathContainer path = parsePath(exchange.getRequest().getURI().getRawPath());
// 跟所有断言去匹配
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path)).findFirst();
// 如果匹配到了,这里做些处理,然后通过loadbalancer去调用Nacos上的微服务
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = optionalPathPattern.get();
traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
}
else {
traceMatch("Pattern", config.getPatterns(), path, false);
return false;
}
}
@Override
public String toString() {
return String.format("Paths: %s, match trailing slash: %b",
config.getPatterns(), config.isMatchOptionalTrailingSeparator());
}
};
}
}
从下面截图中可以看到,是可以拿到请求的url的
所以断言
是很重要的,记下来看下断言都可以配置成什么样的
路由断言工厂(Route Predicate Factories)配置
predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
。application.yml配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
断言配置官方文档
通过网关启动日志,可以查看内置路由断言工厂
路径匹配
spring:
application:
name: mall-gateway
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一,建议配置成微服务名,这里没用微服务名是为了下面说明怎么调用的
uri: lb://mall-order #lb 整合负载均衡器loadbalancer
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
- id: user_route #路由ID,全局唯一,建议配置成微服务名
uri: lb://mall-user #lb 整合负载均衡器loadbalancer
predicates:
- Path=/user/** # 断言,路径相匹配的进行路由
Header匹配(请求头匹配)
配置一个在/order/**
的请求路径中,必须写到X-Request-Id
参数,值为数字类型的断言
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
- id: order_route #路由ID,全局唯一
uri: lb://mall-order #目标微服务的请求地址和端口
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
# Header匹配 请求中带有请求头名为 x-request-id,其值与 \d+ 正则表达式匹配
- Header=X-Request-Id, \d+
请求Headers中,不携带断言参数
请求Headers中,不携带X-Request-Id
参数,会报错,返回404
请求Headers中,携带断言参数
请求Headers中,携带X-Request-Id
参数,会请求成功
请求头源码类
public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
boolean hasRegex = !StringUtils.isEmpty(config.regexp);
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 请求header是否为空
List<String> values = exchange.getRequest().getHeaders()
.getOrDefault(config.header, Collections.emptyList());
// 为空返回false
if (values.isEmpty()) {
return false;
}
// 不为空,判断是否符合断言
// values is now guaranteed to not be empty
if (hasRegex) {
// check if a header value matches
return values.stream()
.anyMatch(value -> value.matches(config.regexp));
}
// there is a value and since regexp is empty, we only check existence.
return true;
}
@Override
public String toString() {
return String.format("Header: %s regexp=%s", config.header,
config.regexp);
}
};
}
@Validated
public static class Config {
@NotEmpty
private String header;
private String regexp;
public String getHeader() {
return header;
}
public Config setHeader(String header) {
this.header = header;
return this;
}
public String getRegexp() {
return regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
}
}
还有其他的一些断言配置,有需要的可以去上面的官方地址查看,这里就不列举了,具体的断言类型下面贴一张官方的图片:
过滤器工厂( GatewayFilter Factories)配置
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
过滤器配置官方文档
下面是官方提供的过滤器类型
添加请求头
yml配置
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-color, blue #添加请求头=X-Request-color,值为blue
代码获取请求头数据
// 获取请求头方式一:request.getHeader
@GetMapping("/testgateway")
public String testGateway(HttpServletRequest request) throws Exception {
log.info("gateWay获取请求头X-Request-color:"+request.getHeader("X-Request-color"));
return "success";
}
// 获取请求头方式二:@RequestHeader注解
@GetMapping("/testgateway2")
public String testGateway(@RequestHeader("X-Request-color") String color) throws Exception {
log.info("gateWay获取请求头X-Request-color:"+color);
return "success";
}
添加请求参数
yml配置
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route #路由ID,全局唯一
uri: https://example.org #目标微服务的请求地址和端口
filters:
- AddRequestParameter=color, blue # 添加请求参数参数名为color,值为blue
@GetMapping("/testgateway3")
public String testGateway3(@RequestParam("color") String color) throws Exception {
log.info("gateWay获取请求参数color:"+color);
return "success";
}
自定义过滤器工厂
继承AbstractNameValueGatewayFilterFactory
且我们的自定义名称必须要以GatewayFilterFactory
结尾并交给spring管理
@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
log.info("调用CheckAuthGatewayFilterFactory==="+ config.getName() + ":" + config.getValue());
// TODO:对配置的参数和值做一些其他额外处理
return chain.filter(exchange);
};
}
}
yml配置
spring:
cloud:
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
#配置过滤器工厂
filters:
- CheckAuth=fox,男 #自定义过滤器工厂
- id: order_route #路由ID,全局唯一
uri: http://localhost:8020 #目标微服务的请求地址和端口
#配置过滤器工厂
filters:
- CheckAuth=sex,男 #自定义过滤器工厂 key是sex,value是男
这种自定义过滤器一般不会使用,因为这种过滤器只是作用于一个服务上,但gateWay给单个服务增加额外过滤器的场景还是比较少的,更推荐的是使用下面的全局过滤器,给所有的微服务增加过滤器
全局过滤器(Global Filters)配置
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
- GatewayFilter:网关过滤器,
需要通过spring.cloud.routes.filters配置在具体的路由下,只作用在当前特定路由上,也可以通过配置spring.cloud.default-filters让它作用于全局路由上
。 - GlobalFilter:全局过滤器,
不需要再配置文件中配置,作用在所有的路由上
,最终通过GatewayFilterAdapter包装成GatewayFilterChain能够识别的过滤器。
ReactiveLoadBalancerClientFilter
ReactiveLoadBalancerClientFilter 会查看exchange的属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值(一个URI,比如lb://mall-order/order/testgateway?color=blue),如果该值的scheme是 lb,比如:lb://myservice ,它将会使
用Spring Cloud的LoadBalancerClient 来将 myservice 解析成实际的host和port。
其实就是用来整合负载均衡器loadbalancer的
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
自定义全局过滤器
自定义全局过滤器定义方式是实现GlobalFilter接口
。每一个过滤器都必须指定一个int类型的order值,order值越小,过滤器优先级越高,执行顺序越靠前
。GlobalFilter通过实现Ordered接口来指定order值
下面是实现一个全局过滤器,用来做安全认证
@Component
@Slf4j
public class CheckAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取token
String token = exchange.getRequest().getHeaders().getFirst("token");
if (null == token) {
log.info("token is null");
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
// 401 用户没有访问权限
response.setStatusCode(HttpStatus.UNAUTHORIZED);
byte[] bytes = HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes();
DataBuffer buffer = response.bufferFactory().wrap(bytes);
// 请求结束,不继续向下请求
return response.writeWith(Mono.just(buffer));
}
//TODO 校验token进行身份认证
log.info("校验token");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 2;
}
}
Gateway基于redis+lua脚本限流
spring cloud官方提供了RequestRateLimiter过滤器工厂,基于redis+lua脚本方式采用令牌桶算法实现了限流
。
去官方文档中找The Redis RateLimiter
即可,请求不被允许时返回状态:HTTP 429 - Too Many Requests。
- pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- 代码yml配置
spring:
application:
name: mall-gateway
data:
#配置redis地址
redis:
host: localhost
port: 6379
database: 0
timeout: 5000
lettuce:
pool:
max-active: 200
max-wait: 10000
max-idle: 100
min-idle: 10
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: order_route #路由ID,全局唯一,建议配置服务名
uri: lb://mall-order #lb 整合负载均衡器ribbon,loadbalancer
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
#配置过滤器工厂
filters: #注意:这里字体变灰是因为上面的Path=/order/**用的同配置**
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 20 #令牌桶的总容量
redis-rate-limiter.requestedTokens: 1 #一个请求花费多少令牌。这是为每个请求从桶中提取令牌的数量,默认为1(可以用默认值,这里可以省略不配)
key-resolver: "#{@keyResolver}" #使用SpEL表达式,从Spring容器中获取Bean对象
- SpEL表达式获取的bean对象
@Bean
KeyResolver keyResolver() {
//url限流
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
//参数限流
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
Gateway整合sentinel限流
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
sentinel网关流控:https://sentinelguard.io/zh-cn/docs/api-gateway-flow-control.html
- pom依赖
<!-- gateway接入sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- yml配置,接入sentinel dashboard,通过sentinel控制台配置网关流控规则
server:
port: 8888
spring:
application:
name: gateway-sentinel-demo
main:
allow-bean-definition-overriding: true
#配置nacos注册中心地址
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 添加sentinel的控制台地址
dashboard: 127.0.0.1:8080
gateway:
routes:
- id: order_route #路由ID,全局唯一,建议配置服务名
uri: lb://mall-order #lb 整合负载均衡器ribbon,loadbalancer
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
- id: user_route #路由ID,全局唯一,建议配置服务名
uri: lb://mall-user #lb 整合负载均衡器loadbalancer
predicates:
- Path=/user/** # 断言,路径相匹配的进行路由
注意:基于SpringBoot3的 Spring Cloud Gateway和Sentinel还存在兼容性问题,等待Sentinel官方对最新的Gateway适配包更新(限流时,会生成报错日志,NoSuchMethodException
)
在Sentinel控制台,可以基于routes去配置限流,也可以自定义API维度去配置限流
Sentinel网关流控实现原理
当通过 GatewayRuleManager 加载网关流控规则(GatewayFlowRule)时,无论是否针对请求属 性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule)
,存储在 GatewayRuleManager 中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx),并同步到生成的热点参数规则中。
外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行 路由/API 分组匹配、请
求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序
组装参数数组,最终传入 SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模块向
Slot Chain 中添加了一个 GatewayFlowSlot,专门用来做网关规则的检查。GatewayFlowSlot 会
从 GatewayRuleManager 中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。
Gateway跨域资源共享配置(CORS Configuration)(了解)
在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制
同源策略(Same Orgin Policy)是一种约定,它是浏览器核心也最基本的安全功能,它会阻止一个域的js脚本和另外一个域的内容进行交互,
如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击
。所谓同源(即在同一个域)就是两个页面具有相同的协议(protocol)、主机(host)和端口号(port)。
如何解决gateway跨域问题
- 方式一:yml配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
- 方式二:java代码配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
注意:
- 生产环境绝对不可能按上面配置所有请求
跨域
- 如果配置的又过滤器,请求过了过滤器才会到跨域(之前测试时,因为请求忘记符合过滤器要求,所以以为配置跨域没生效)