介绍
API 网关是一个服务,是系统的唯一入口。
Spring Cloud Gateway 基于 Spring Boot 2,是 Spring Cloud 的全新项目。Gateway 旨在提供一种简单而有效的途径来转发请求,并为它们提供横切关注点。
重要概念
-
路由 Route:路由是网关最基础的部分,路由信息由一个 ID 、一个目的 URL 、一组断言工厂和一组 Filter 组成。如果路由断言为真,则说明请求的 URL 和配置的路由匹配。
-
断言 Predicate:Java 8 中的断言函数。Spring Cloud Gateway 中的断言函数输入类型是 Spring 5.0 框架中的 ServerWebExchange 。Spring Cloud Gateway 中的断言函数允许开发者去定义匹配来自 Http Request 中的任何信息,比如请求头和参数等。
-
过滤器 Filter:一个标准的 Spring Web Filter。Spring Cloud Gateway 中的 Filter 分为两种类型:Gateway Filter 和 Global Filter。过滤器 Filter 将会对请求和响应进行修改处理。
功能
作为网关来说,网关最重要的功能就是协议适配和协议转发,协议转发也就是最基本的路由信息转发。
注解
@order 代表的优先级是从小往大排序的,即数值越小,优先级越高
引入依赖
注意:Gateway 自己使用了 netty 实现了 Web 服务,此处『不需要引入 Spring Web』,如果引入了,反而还会报冲突错误,无法启动。
把tomcat移除出去:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
yml配置
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: 163_route
uri: http://www.163.com
predicates:
- Path=/163
当用户输入 /163 路径时,Gateway 将会导向到 http://www.163.com 网址。
Path 路由断言
前面 163
的示例中,我们使用的就是 Path 路由断言
spring:
cloud:
gateway:
routes:
- id: <id> # 路由 ID,唯一
uri: <目标URL> # 目标 URI,路由到微服务的地址
predicates:
- Path=<匹配规则> # 支持通配符
uri 也可以写成为微服务的服务名,语法: uri: lb://微服务名
注意:
Path 断言不会改变请求的 URI ,即,Gateway 收到的 URI 是什么样的,那么它将请求转给目标服务的时候,URI 仍然是什么。整个过程中只有 IP、端口部分会被『替换』掉 。这和 Zuul 网关默认会截断一段 URI 的行为『刚好相反』。
再重复一遍:Path 断言不会改变请求的 URI ,整个过程中只有 IP、端口部分会被『替换』掉 。
其它断言
After 路由断言
After 路由断言会要求你提供一个 UTC 格式的时间,当 Gateway 接收到的请求时间在配置的 UTC 时间之后,则会成功匹配,予以转发,否则不成功。
Before 路由断言
Before 路由断言和之前的 After 路由断言类似。它会取一个 UTC 时间格式的时间参数,当请求进来的当前时间在配置的时间之前会成功(放行),否则不能成功。
Cookie 路由断言
Cooke 路由断言会取两个参数:HTTP 请求所携带的 Cookie 的 key 和 value。当请求中携带的 cookie 和 Cookie 路由断言中配置的 cookie 一致时,路由才匹配成功。
Header 路由断言
Header 路由断言用于根据 HTTP 请求的 header 中是否携带所配置的信息与否,来决定是否通过断言。
Method 路由断言
Method 路由断言会根据路由信息所配置的 method 对请求方式是 GET 或者 POST 等进行断言匹配。
Query 路由断言
Query 断言会从请求中获取两个参数,将请求参数和 Query 断言中的配置进行匹配。
例如,http://localhost:9000/test?username=tom 中的 username=tom 和 r.query(“username”, “tom”) 匹配。
LoginFilter代码部分
package com.woniu.gateway.filter;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 认证
*
* @Order(1):值越小,优先级越高
*/
@Component
@Order(1)
public class LoginFilter implements GlobalFilter {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String jwt = request.getHeaders().getFirst("token");
if (StringUtils.isEmpty(jwt) || !JWTUtil.verify(jwt, "ok".getBytes())) {
String path = request.getPath().toString();
List<String> list = new ArrayList<>();
list.add("/login");
if (list.contains(path)) {
return chain.filter(exchange);
}
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);//403,无权限
return response.setComplete();
}
return chain.filter(exchange);
}
}