核心部分:routes(路由), predicates(断言),filters(过滤器)
id:可以理解为是这组配置的一个id值,请保证他的唯一的,可以设置为和服务名一致
uri:可以理解为是通过条件匹配之后需要路由到(跳转,重定向)到的新的服务地址
predicates:可以理解为是编写条件,满足条件才进行uri
filters:可以理解为是在路由前对请求的地址进行额外的其他操作,例如拼接或者裁减等
pom依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml配置网关
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
gateway:
routes: #路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route #当前路由的标识,要求唯-
uri: http://localhost:8086 #请求要转发到的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/product-service/** #当请求路径满足Path指定的规则时,才进行路由转发
#http://localhost:8087/product-service/product/9 路由器到⬇
#http://localhost:8086/product-service/product/9
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径
##http://localhost:8086/product/9
yml注册到nacos配置网关
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
routes: #路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route #当前路由的标识,要求唯-
uri: lb://product_naocs #lb指的是从nacos中按名称获取微服务,并遵循负载均衡策略
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/product-service/** #当请求路径满足Path指定的规则时,才进行路由转发
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径,
yml配置网关(约定大于配置的方式,一般不用这样的配置),例如访问http://localhost:8087/product-service/product/9 ,中的product-service为服务的名字
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: true #是否启动自动识别nacos服务(约定大于配置)
注意:若要使用自定义配置,则"不能开启注册中心的路由功能",否则自定义的策略会失,设置中心路由功能为false
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: false #是否启动自动识别nacos服务(约定大于配置)
查看网关内置断言官网文档,如果用上时间可以设置获取使用方法:ZonedDateTime.now(),获取
内置断言例子,日期2024-01-20T17:42:47都可以访问,超过时间访问返回404
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
routes: #路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route #当前路由的标识,要求唯-
uri: http://localhost:8086 #请求要转发到的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/product-service/** #当请求路径满足Path指定的规则时,才进行路由转发
- Before=2024-08-20T17:42:47.789-07:00[America/Denver]
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径,
- id: stock-nacos #当前路由的标识,要求唯-
uri: http://localhost:8085 #请求要转发到的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/stock-nacos/** #当请求路径满足Path指定的规则时,才进行路由转发
- Before=2024-08-20T17:42:47.789-07:00[America/Denver]
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径,
内部断言的类名是有断言前缀和后缀组成如下
Path+RoutePredicateFactory
RemoteAddr+RoutePredicateFactory
RemoteAddr+RoutePredicateFactory
Header+RoutePredicateFactory
...
基于远程地址的断言工厂
- RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
yml
- RemoteAddr=192.168.1.1/24
基于Cookie的断言工厂
- CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。
yml
- RemoteAddr=chocolate, ch.
基于Header的断言工厂
- HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式,判断请求Header是否具有给定名称且值与正则表达式匹配。
yml
- Header=X-Request-Id,\d+
基于Host的断言工
- HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则
yml
- Host=**.testhost.org
基于Method请求方法的
基于Method请求方法的断言工厂
- MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配
yml
- Method=GET
### 基于Path请求路径的断言工厂
- PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
yml
#多个路径使用逗号,隔开
- Path=/foo/{segment}1 , /product/add
基于Query请求参数的断言工厂
- QueryRoutePredicateFactory:接收两个参数,请求param和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配,
yml
- Query=baz, ba.
基于Path请求路径的断言工厂
自定义路由断言工厂
可以查看路径路由的断言工厂,选择AbstractRoutePredicateFactory按Ctrl+H既可以显示出全部的路由断言工厂,可以复制一份修改成自定义的断言工厂
自定义路由断言工厂需要继承 AbstadRoutepredicatefacdoy类,重写 ap)y方法的透辑,在apy方法中可以通过 excthange.geiequest) 拿到 serneiHpReque 对象,从而可以获取到清求的参数、请求方式、请求头等信息
- 必须spring组件 bean
- 类必须加上RoutePredicateFactory作为结尾(底层的处理的约定大于配置自定会读取到的)
- 必须继承AbstractRoutePredicateFactory
- 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
- 需要结合shortcutFieldOrder进行绑定
- 通过apply进行逻辑判断 true就是匹配成功 false匹配失败
自定义路由断言工厂类
java
package com.test.gate.config;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* @Description:
* @Author: xu
* @Data: 2024-2024/4/11-21
* @Version: V1.0
*/
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(Config.class);
}
public List<String> shortcutFieldOrder() {
return Arrays.asList("param");
}
public Predicate<ServerWebExchange> apply(final Config config) {
return new GatewayPredicate() {
public boolean test(ServerWebExchange exchange) {
if (config.getParam().equals("zhangSan")) {
return true;
}
return false;
}
public Object getConfig() {
return config;
}
public String toString() {
return String.format("Query: param=%s regexp=%s", config.getParam());
}
};
}
@Validated
public static class Config {
private String param;
public Config() {
}
public String getParam() {
return this.param;
}
public void setParam(String param) {
this.param = param;
}
}
}
yml配置(如果一个路由里面有多个断言条件是且的关系,都需要满足才跳转uri)
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
# discovery:
# locator:
# enabled: true #是否启动自动识别nacos服务(约定大于配置)
routes: #路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route #当前路由的标识,要求唯-
uri: http://localhost:8086 #请求要转发到的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/product-service/**,/ddd/s #当请求路径满足Path指定的规则时,才进行路由转发
- Before=2024-08-20T17:42:47.789-07:00[America/Denver]
- CheckAuth=zhangSan1
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径,
查看网关各种局部过滤器官网
自定义网关过滤器和自定义断言都全部类似
继承AbstractNameValueGatewavFilterFactory日我们的自定义名称必须要以GatewavFiterFactorv结尾并交给spring管理
java
package com.test.gate.config;
import com.alibaba.nacos.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* @Description:
* @Author: xu
* @Data: 2024-2024/4/12-11
* @Version: V1.0
*/
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
private static Logger log = LoggerFactory.getLogger(CheckAuthGatewayFilterFactory.class);
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
log.info("调用checkAuthGatewayFilterFactory==="
+ config.getName());
String name = exchange.getRequest().getQueryParams().getFirst("name");
if (StringUtils.isNotBlank(name)) {
if (config.getName().equals(name)) {
chain.filter(exchange);
} else {
//返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
exchange.getResponse().setComplete();
}
//1 正常请求
chain.filter(exchange);
}
return null;
};
}
public static class Config {
private String name;
public Config() {
}
public String getName() {
return this.name;
}
public void setName(String prefix) {
this.name = name;
}
}
}
yml
server:
port: 8087
spring:
application:
name: gateway-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.11.47:18848
username: nacos
password: nacos
gateway:
# discovery:
# locator:
# enabled: true #是否启动自动识别nacos服务(约定大于配置)
routes: #路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route #当前路由的标识,要求唯-
uri: http://localhost:8086 #请求要转发到的地址
order: 1 #路由的优先级,数字越小级别越高
predicates: #断言(就是路由转发要满足的条件)
- Path=/product-service/**,/ddd/s #当请求路径满足Path指定的规则时,才进行路由转发
- Before=2024-08-20T17:42:47.789-07:00[America/Denver]
- CheckAuth=zhangSan1
filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 #转发之前去掉1层路径,
- CheckAuth=lisi
全局过滤器(Global Filters)配置
自定义全局过滤器(Global Filters)配置类(一般都是实现认证或者授权,或者做日记记录,不需要在配置文件做任何配置)
java
package com.test.gate.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description:
* @Author: xu
* @Data: 2024-2024/4/12-20
* @Version: V1.0
*/
@Component
public class LogGlobalFilter implements GlobalFilter {
private static Logger log = LoggerFactory.getLogger(LogGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//做业务处理,授权或者日记记录
log.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
要启用 Reactor Netty 访问日志,请设置-Dreactor,netty.http,server.accessLogEnabled=true,它必须是 Java 系统属性,而不是 Spring Boot 属性。如果是jar包启动,就将网关打包成jar包,如下运行
bash
java -jar gateway.jar -Dreactor,netty.http,server.accessLogEnabled=true
idea配置网关日记打印,在vm配置 -Dreactor.netty.http.server.accessLogEnabled=true
将网关日志记录全部输出到日志文件。以下示例创建一个Logback.xml 配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按天滚动的文件输出 -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>G:\temptemptemp\gatewaylog\logfile.log</file> <!-- 修改为你想要保存日志文件的路径 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>G:\temptemptemp\gatewaylog\logfile.%d{yyyy-MM-dd}.log
</fileNamePattern> <!-- 指定按日期滚动的文件名格式 -->
<maxHistory>30</maxHistory> <!-- 保留最多30天的历史日志文件 -->
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 异步日志输出 -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="ROLLING_FILE"/> <!-- 将异步 appender 链接到文件输出 -->
</appender>
<!-- 记录器配置 -->
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/> <!-- 将异步 appender 应用于特定的日志记录器 -->
<appender-ref ref="STDOUT"/> <!-- 控制台输出 -->
</logger>
<!-- 根日志级别设置 -->
<root level="info">
<appender-ref ref="STDOUT"/> <!-- 控制台输出 -->
<appender-ref ref="ROLLING_FILE"/> <!-- 将按天滚动的文件输出器添加到根输出器中 -->
</root>
</configuration>
Gateway跨域配置(CORS Configuration),网关官网跨域配置
方法一:yml配置文件配置跨域
yml
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': #运行跨域访问的资源
allowedOrigins: "https://docs.spring.io" #跨域允许来源
allowedMethods:
- GET
- POST
方法二:通过配置类设置跨域(注意UrlBasedCorsConfigurationSource 导入的包是import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource)
java
package com.test.gate.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @Description:
* @Author: xu
* @Data: 2024-2024/4/12-22
* @Version: V1.0
*/
@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);
}
}