定制软件网关GateWay的使用详解、路由、过滤器、跨域配置

GateWay定制软件的使用详解、路由、过滤器、跨域配置

一、定制软件网关的基本概念

SpringCloudGateway定制软件网关是所有的统一入口。

1.1 定制软件它的主要作用是:

  • 反向代理(定制软件请求的转发)
  • 定制软件路由和负载均衡
  • 定制软件身份认证和权限控制
  • 定制软件对请求限流

1.2 相比于Zuul的优势:

SpringCloudGateway基于Spring5中提供的WebFlux,定制软件是一种响应式编程的实现,定制软件性能更加优越。

Zuul定制软件的实现方式比较老式,基于Servlet的实现,定制软件它是一种阻塞式编程,定制软件在高并发下性能性能不佳。

拓展:

其实Nginx定制软件也可以作为网关,定制软件但是要使用Nginx定制软件自主实现网关的相关功能,定制软件还需要借助lua脚本语言

定制软件学习成本是比较高的,定制软件现在一般也不会使用它来做网关,定制软件但是只按性能来讲Nginx,定制软件性能是最高的。

1.3 SpringCloudGateway架构图:

定制软件微服务只接收来自网关的请求,定制软件而其它直接访问微服务定制软件本身的请求拒绝。

定制软件这样可以极大保护微服定制软件务免受不法侵害。

定制软件同时在请求压力激增时,定制软件可以实施服务限流,定制软件保护微服务集群。

二、SpringBoot中配置GateWay

2.1 引入GateWay的Maven依赖

<!--网关 起步依赖--><dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos服务发现 起步依赖--><dependency>    <groupId>com.alibaba.cloud</groupId>    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.2 配置application.yml文件

server:  port: 10086 		# 网关端口spring:  application:    name: gateway	# 服务名称  cloud:    nacos:      server-addr: localhost:8848 		 # nacos 地址    gateway:      routes: 							 # 网关路由配置        - id: user-service 				 # 路由id,自定义,只要唯一即可          # uri: http://127.0.0.1:8081 	 # 路由的目标地址 (直接写死地址的方式,不推荐)          uri: lb://userservice 		 # 路由的目标地址 lb是负载均衡,后面跟服务名称(推荐)          predicates:			 		 # 路由断言,判断请求是否符合路由规则的条件            - Path=/user/** 	 		 # 按照路径匹配,以/user/开头的请求就符合要求        - id: card-service          uri: lb://cardservice          predicates:            - Path=/card/**
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

gateway配置中的注意点:

  • routes 后面的路由可以配置多个,相当于配置个数组,一个-开头的配置就是其中的一个数组元素。

  • uri为什么选择以服务名+负载均衡的方式?

    主要是写死地址的话,今后如果userservice的地址变了,那么又要去修改yml配置文件。

    lb://userservice可以让程序员一眼认出这是哪个微服务,以后地址变了也无需修改yml配置文件。

上述配置详解:

/user/**开头的请求,代理到lb://userservice

/card/**开头的请求,代理到lb://cardservice

lb是负载均衡,根据服务名拉取服务列表,实现负载均衡。

  1. http://127.0.0.1:10086/user/99 就算是/user/**开头的请求,不要把协议、ip和端口计算在内。
  2. 有多少个需要配置的路由,都按上面的格式配置即可

三、GateWay路由配置详解

路由主要有四个配置:

  • 路由id(id

  • 路由目标(uri

  • 路由断言(predicates):判断路由的规则,

  • 路由过滤器(filters):对请求或响应做处理

3.1 路由id

当前路由的唯一标识。

3.1 路由目标

路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡。

一般都不会选择写死http固定地址的方式。而是选择可维护性更强的lb根据服务名负载均衡的方式。

具体优势如上所言。

3.3 路由断言

路由断言主要用来判断路由的规则。

配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理。

例如Path=/user/**是按照路径匹配,这个规则是由

org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理。

像这样的断言工厂在SpringCloudGateway还有十几个:

名称说明示例
After是某个时间点后的请求- After=2022-01-20T14:32:27.789-07:00[Asia/Shanghai]
Before是某个时间点之前的请求- Before=2022-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2021-01-20T17:42:47.789-07:00[Asia/Shanghai], 2023-01-21T17:42:47.789-07:00[Asia/Shanghai]
Cookie请求必须包含某些cookie- Cookie=chocolate
Header请求必须包含某些header- Header=asd, cas
Host请求必须是访问某个host(域名)- Host=baidu.com, jd.com
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/user/{params},/card/**
Query请求参数必须包含指定参数- Query=name, Jack
RemoteAddr请求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24
Weight权重处理

实际使用时,根绝业务要求选择使用即可。

不过一般来讲,最常用的是使用Path这种断言工厂,仅用它就能满足常见的需求了。

关于Path断言工厂的补充:

Path=/card/**代表 以/card/路径开头的多级路径请求,这么写多级路径请求和一级路径请求都生效。

Path=/card/*代表 以/card/路径开头的一级路径请求,这么写多级路径请求将不会生效。

断言工厂官方文档:

https://docs.spring.io/spring-cloud-gateway/docs/3.1.4-SNAPSHOT/reference/html/#gateway-request-predicates-factories

(今后如果有复杂的断言工厂配置,可以参照官网文档上的例子去实现。)

3.4 路由过滤器(filters)

路由过滤器对请求或响应做处理。

客户端请求先找到路由,路由匹配时经过过滤器层层筛选,最终访问到微服务。

当然微服务的请求反悔时,也会经过过滤器的筛选,只不过我们一般只对请求过滤,而不会对响应过滤。

SpringCloudGateWay目前已经提供了34种不同的过滤器工厂。

常用的几个有:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

3.4.1 请求头过滤器配置示例(局部过滤器)

spring:  cloud:    gateway:      routes:      - id: user-service         uri: lb://userservice         predicates:         - Path=/user/**         filters: 					   # 过滤器配置        - AddRequestHeader=token, test # 添加请求头
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上述过滤器的含义:

给所有进入userservice的请求添加一个请求头。

请求头的keytokenvaluetest

由于当前前过滤器写在微服务的userservice路由下,因此仅仅对访问微服务userservice的请求有效。

3.4.2 默认过滤器配置示例(全局过滤器)

spring:  cloud:    gateway:      routes:        - id: user-service           uri: lb://userservice           predicates:           - Path=/user/**      default-filters: 					# 默认过滤器配置        - AddRequestHeader=token, test  # 添加请求头
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

default-filters的配置和routes平级。

只要配置在default-filters下面的过滤器,会对routes配置的所有路由都生效。

过滤器工厂官方文档:

https://docs.spring.io/spring-cloud-gateway/docs/3.1.4-SNAPSHOT/reference/html/#gateway-request-predicates-factories
  • 1

今后如果有复杂的断言工厂配置,可以参照官网文档上的例子去实现。

四、自定义全局路由过滤器

有时候SpringCloudGateWay提供的过滤器工厂不能满足自己的要求。

可能有时候需要在过滤时做一些其它的逻辑操作。

那么这时候可以选择使用java代码自定义全局过滤器。

代码示例:

@Componentpublic class GateWayFilter implements GlobalFilter, Ordered {    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        // 1.获取请求参数         //1.这里的request并不是servlet中的request          //2.返回值是一个多键的map集合、也就是说这个map集合的键可以重复        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();        // 2.获取userName参数        String userName = params.getFirst("userName");        // 3.校验        if ("root".equals(userName)) {            // 放行            return chain.filter(exchange);        }        // 4.拦截        // 4.1.禁止访问,设置状态码        exchange.getResponse().setStatusCode(500);        // 4.2.结束处理        return exchange.getResponse().setComplete();    }    @Override    public int getOrder() {        return -1;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

当有多个过滤器时,Order的值决定了过滤器的执行顺序。

数值越大优先级越低, 负的越多, 优先级越高。

设置Order的值有两种方式:

1. 实现Ordered接口,并且重写getOrder方法

@Componentpublic class GateWayFilter implements GlobalFilter, Ordered {    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {    }    @Override    public int getOrder() {        return -1;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. 使用@Order注解

@Order(-1)@Componentpublic class GateWayFilter implements GlobalFilter, Ordered {    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

五、过滤路由过滤器的执行顺序

5.1 过滤器的种类

SpringCloudGateWay中,有三种过滤器:

  • 默认过滤器default-filters

  • 只对具体某个路由生效的局部过滤器filters

  • 使用java代码编写的全局过滤器GlobalFilter

5.2 过滤器的执行顺序

由上图知过滤器的执行顺序为:默认过滤器 → 当前路由过滤器 → 全局过滤器

六、网关的跨域问题

6.1 跨域的概念和原理

跨域:请求位置和被请求位置不同源就会发生跨域。

这里的不同源包括两个点:

  1. 域名不同:www.baidu.comwww.taobao.com。(IP不同也是相同道理)
  2. 端口不同:127.0.0.1:8080127.0.0.1:8081

而浏览器又会禁止请求的发起者与服务端发生跨域AJAX请求。

如果发生了跨域请求,服务器端是能够正常响应的,但是响应的结果会被浏览器拦截

6.2 跨域常见解决方案

使用CORS方式。

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

6.3 gateway中如何解决跨域问题

方式一:配置application.yml文件:

spring:  cloud:    gateway:      globalcors: # 全局的跨域配置        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题        							# options请求 就是一种询问服务器是否浏览器可以跨域的请求        							# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗        							# 可以配置本次跨域检测的有效期maxAge        							# 在maxAge设置的时间范围内,不去询问,统统允许跨域        corsConfigurations:          '[/**]':            allowedOrigins: 		# 允许哪些网站的跨域请求               - "http://localhost:8090"            allowedMethods: 		# 允许的跨域ajax的请求方式              - "GET"              - "POST"              - "DELETE"              - "PUT"              - "OPTIONS"            allowedHeaders: "*" 	# 允许在请求中携带的头信息            allowCredentials: true	# 允许在请求中携带cookie            maxAge: 360000 			# 本次跨域检测的有效期(单位毫秒)            						# 有效期内,跨域请求不会一直发option请求去增大服务器压力
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

方式二:使用编码方式定义配置类:

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.web.cors.reactive.CorsUtils;import org.springframework.web.server.ServerWebExchange;import org.springframework.web.server.WebFilter;import org.springframework.web.server.WebFilterChain;import reactor.core.publisher.Mono;@Configurationpublic class CorsConfig {    private static final String MAX_AGE = "18000L";    @Bean    public WebFilter corsFilter() {        return (ServerWebExchange ctx, WebFilterChain chain) -> {            ServerHttpRequest request = ctx.getRequest();            // 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域            if (!CorsUtils.isCorsRequest(request)) {                return chain.filter(ctx);            }            HttpHeaders requestHeaders = request.getHeaders();                                  // 获取请求头            ServerHttpResponse response = ctx.getResponse();                                    // 获取响应对象            HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();          // 获取请求方式对象            HttpHeaders headers = response.getHeaders();                                        // 获取响应头            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());   // 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());            if (requestMethod != null) {                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());    // 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)            }            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");       			// 允许在请求中携带cookie(相当于yml中的allowCredentials)            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");             			// 允许在请求中携带的头信息(相当于yml中的allowedHeaders)            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);                           // 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)            if (request.getMethod() == HttpMethod.OPTIONS) {                                    // 直接给option请求反回结果                response.setStatusCode(HttpStatus.OK);                return Mono.empty();            }            return chain.filter(ctx);                                                           // 不是option请求则放行        };    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发