简介
Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。 Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护。Spring Cloud Gateway本身也是一个微服务,需要注册到Eureka服务注册中心。
核心功能: 过滤和路由
核心概念
路由(route): 路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。
断言(Predicate): Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何信息比如请求头和参数。
过滤器(Filter) :一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。
快速入门案例
创建itkaoti-getway模块
- 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- GetwayApplicationGetwayApplication
@SpringBootApplication
@EnableDiscoveryClient
public class GetwayApplication {
public static void main(String[] args) {
SpringApplication.run(GetwayApplication.class, args);
}
}
- 配置文件
端口: 10010, 其中routes的配置, 表示 所有通过getway的流量路径符合/user/** 的都会转发到8080端口服务
server:
port: 10010
spring:
application:
name: api-getway
cloud:
gateway:
routes:
# 路由id
- id: user-service-route
# 代理的服务地址
uri: http://127.0.0.1:8080
# 路由断言,可以配置映射路径
predicates:
- Path=/user/**
# 配置eureka
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
# 配置ip
ip-address: 127.0.0.1
# 优先使用ip
prefer-ip-address: true
# 一旦超过多长时间, eureka认为服务已经移除或者失效, 默认: 90s
lease-expiration-duration-in-seconds: 30
# 服务的续约时间(多久续约一次) 默认:30s
lease-renewal-interval-in-seconds: 10
- 启动
我们启动 eureka(10086), user-service(8080) 以及getway(10010),
那么此时我们访问: http://127.0.0.1:10010/user/2 --> http://127.0.0.1:8080/user/2
我们发现访问成功, 请求成功转发到8080端口
面向服务路由
itkaoti-getway
在上面入门工程中, 我们将路由地址是写死是不合理的, 在springcloudgetway, 我们可以通过配置动态路来解决, 我们可以通过服务的名称去uereka中获取。
- 修改配置文件
将写死的uri修改为如下
uri: lb://user-service
- 重启getway
访问 http://127.0.0.1:10010/user/2
路由器的前缀处理
有时候我们访问的路径和转发的路由有一定的差异, 比如 getway http://127.0.0.1:10010/api/user/2, 而实际上我们的服务为 http://127.0.0.1:8080/user/2, 那该怎么办呢? 当然有多处前缀, 也有可能路径少一些, 比如: http://127.0.0.1:10010/2, 这个链接直接对应我们的服务。
添加前缀
我们访问 http://127.0.0.1:10010/2 , getway转发请求访问 http://127.0.0.1:8080/user/2
- 配置文件
spring:
application:
name: api-getway
cloud:
gateway:
routes:
# 路由id
- id: user-service-route
# 代理的服务地址
# uri: http://127.0.0.1:8080
uri: lb://user-service
# 路由断言,可以配置映射路径
predicates:
- Path=/**
filters:
# 添加请求的前缀
- PrefixPaht=/user
- 重启测试
访问 http://127.0.0.1:10010/2, 访问ok
去除前缀
我们访问 http://127.0.0.1:10010/api/user/2 , getway转发请求访问 http://127.0.0.1:8080/user/2
- 修改配置文件
spring:
application:
name: api-getway
cloud:
gateway:
routes:
# 路由id
- id: user-service-route
# 代理的服务地址
# uri: http://127.0.0.1:8080
uri: lb://user-service
# 路由断言,可以配置映射路径
predicates:
- Path=/api/user/**
filters:
# 表示过滤1个路径,2表示两个路径,以此类推
- StripPrefix=1
- 重启
访问 http://127.0.0.1:10010/api/user/2, 访问ok
过滤器
简介
Gateway作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现的。前面的 路由前缀 章节中的功能也是使用过滤器实现的。
过滤器类型:Gateway实现方式上,有两种过滤器;局部过滤器 以及 全局过滤器
- 局部过滤器
通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由上;自带的过滤器都可以配置或者自定义按照自带过滤器的方式。如果配置spring.cloud.gateway.default-filters 上会对所有路由生效也算是全局的过滤器;但是这些过滤器的实现上都是要实现GatewayFilterFactory接口。
- 全局过滤器
不需要在配置文件中配置,作用在所有的路由上;实现 GlobalFilter 接口即可。
快速案例
实现添加响应头的过滤器
- 修改配置文件
这边我们使用AddResponseHeader 过滤器来添加2个响应头
spring:
application:
name: api-getway
cloud:
gateway:
routes:
# 路由id
- id: user-service-route
# 代理的服务地址
uri: lb://user-service
#添加前缀-*-*-*-*-*-*-*-*
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- AddResponseHeader=X-Response-A, B
- AddResponseHeader=itkaoti, B
- 启动
访问 http://127.0.0.1:10010/api/user/2, 访问ok
自定义局部过滤器
要求
访问http://127.0.0.1:10010/api/user/2 的链接的时候,要求添加token 参数来做用户登录校验, token=123456的时候, 才能返回正确数据,否则返回权限不够的提示。
(可以仿照 AddResponseHeaderGatewayFilterFactory, 来实现)
配置文件
添加TokenCheck=token , springcloud会根据TokenCheck 去查找 TokenCheckGatewayFilterFactory类
spring:
application:
name: api-getway
cloud:
gateway:
routes:
# 路由id
- id: user-service-route
# 代理的服务地址
uri: lb://user-service
#添加前缀-*-*-*-*-*-*-*-*
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- AddResponseHeader=X-Response-A, B
- AddResponseHeader=itkaoti, B
- TokenCheck=token
TokenCheckGatewayFilterFactory
@Component
public class TokenCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenCheckGatewayFilterFactory.ParamConfig> {
// 这个必须要加,否则下面apply方法中的config.getParam() 获取为null
static final String PARAM_NAME = "param";
public List<String> shortcutFieldOrder() {
return Arrays.asList(PARAM_NAME);
}
// 构造函数必须加, 否则会报类型转换异常
public TokenCheckGatewayFilterFactory() {
super(TokenCheckGatewayFilterFactory.ParamConfig.class);
}
public static class ParamConfig {
protected String param;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
}
@Override
public GatewayFilter apply(ParamConfig config) {
return (exchange, chain) -> {
// config.getParam() --> 相当于获取到了配置文件中的TokenCheck=token 的值, 也就是token
// 下面就是获取了token的值
List<String> strings = exchange.getRequest().getQueryParams().get(config.getParam());
boolean b = strings.stream().anyMatch(string -> string.equals("123456"));
if(b){
return chain.filter(exchange);
}else{
// 这边处理有点问题, 仅仅实现了不让访问
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
};
}
}
- 启动
访问 http://127.0.0.1:10010/api/user/2?token=123456 正常访问
访问 http://127.0.0.1:10010/api/user/2?token=12345, token不对 正常不了
自定义全局过滤器
编写全局过滤器
/**
* 全局过滤器, 过滤referer非itkaoti的请求
*/
@Component
public class RefererCheckGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("---------------全局过滤器-------------------");
String referer = exchange.getRequest().getHeaders().getFirst("referer");
if(StringUtils.isBlank(referer) || !referer.contains("itkaoti.top")){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 值越小, 越先执行
return 1;
}
}
启动
这个时候, 我们就需要使用postman来伪造 referer 来请求
http://127.0.0.1:10010/api/user/2?token=123456
在我们不添加referer的时候, 请求响应401,
添加referer后, 正常返回请求结果
getWay的其他参数说明
负载均衡
# 负载均衡
ribbon:
ConnectTimeout: 1000
ReadTimeout: 2000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
熔断配置
# 熔断配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
跨域配置
spring:
cloud:
gateway:
# 跨域设置
globalcors:
corsConfigurations:
'[/**]':
#allowedOrigins: * # 这种写法或者下面的都可以,*表示全部
allowedOrigins:
- "http://*.itkaoti.top"
allowedMethods:
- GET
getWay高可用
启动多个getWay, 使用nginx 做负载均衡