目录
- 一、网关作用
- 二、网关的技术实现
- 三、简单使用
- 四、predicates
- (1) 网关路由可配置的内容
- (2) 路由断言工厂(Route Predicate Factory)
- 五、filters
- (1) GatewayFilter
- (2) 给全部进入 userservice 的请求添加请求头
- (3) 全局过滤器 —— GlobalFilter
- (4) 过滤器的执行顺序
- 六、网关跨域
- (1) 跨域
- (2) 网关跨域处理
一、网关作用
📗 身份认证、权限校验
📗 服务路由、负载均衡
📗 请求限流
二、网关的技术实现
📝 在 SpringCloud 中有两种网关的技术实现
✏️ gateway(比 zuul 新)
✏️ zuul
✒️ Zuul 是基于 Servlet 实现的,属于阻塞式编程
✒️ Gateway 是基于 Spring5 中提供的 WebFlux 实现的。属于响应式编程,具有更好的性能
三、简单使用
✏️ 网关是一个新的微服务
✏️ 需要通过 nacos 进行服务注册和发现
🎄 (1) 创建新的模块(module),引入 SpringCloudGateway 和 Nacos 服务发现依赖
<dependencies>
<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>
</dependencies>
🎄 (2)把网关服务注册到 nacos 中,并编写路由配置
server:
port: 10010 # 网关服务的端口号
spring:
application:
name: gateway # 服务名
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务发现地址
gateway: # 网关相关配置
routes: # 网关的路由配置
- id: userservice # 路由编号(自定义), 保持唯一
uri: lb://userservice # 路由的目标地址【lb: load balance】
# uri: http://127.0.01:8081 # 路由的目标地址(直接写死)
predicates: # 判断(断言)怎样的请求会被路由
- Path=/users/** # 若请求路径是【/users/】开头的就路由到此
- id: orderservice
uri: lb://orderservice
predicates:
- Path=/orders/**
四、predicates
(1) 网关路由可配置的内容
✒️ id
: 路由的唯一标识
✒️ uri
:路由目的地【① lb: load balance;② http(不推荐)】
✒️ predicates
:路由断言(判断请求是否符合要求,符合则路由转发到对应的 uri 的服务器)
✒️ filters
:路由过滤器(处理请求或响应)
(2) 路由断言工厂(Route Predicate Factory)
📝 在配置文件中写的断言规则只是字符串(它会被 Predicate Factory 读取和处理,并转变为路由判断的条件)
✏️ Path=/user/**
是按照路径进行匹配
✏️ 是通过 PathRoutePredicateFactory
类来处理的
✏️ SpringCloudGateway 中的断言工厂:
五、filters
(1) GatewayFilter
📝 GatewayFilter 是网关中提供的一种过滤器(可对进入网关的请求和微服务的响应做处理)
官方提供了三十多种过滤器工厂:
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
(2) 给全部进入 userservice 的请求添加请求头
spring:
application:
name: gateway # 服务名
cloud:
gateway: # 网关相关配置
routes: # 网关的路由配置
- id: userservice # 路由编号(自定义), 保持唯一
uri: lb://userservice # 路由的目标地址【lb: load balance】
# uri: http://127.0.01:8081 # 路由的目标地址(直接写死)
predicates: # 判断(断言)怎样的请求会被路由
- Path=/users/** # 若请求路径是【/users/】开头的就路由到此
filters: # 过滤器
- AddRequestHeader=color, skyblue
- id: orderservice
uri: lb://orderservice
predicates:
- Path=/orders/**
@GetMapping("getUserById/{id}")
public User getUserById(@PathVariable Long id,
@RequestHeader(value = "color", required = false) String color) {
log.info("请求头: {}", color);
return userService.getUserById(id);
}
默认过滤器 default-filters
会对全部的路由请求都生效
spring:
application:
name: gateway # 服务名
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务发现地址
gateway: # 网关相关配置
routes: # 网关的路由配置
- id: userservice # 路由编号(自定义), 保持唯一
uri: lb://userservice # 路由的目标地址【lb: load balance】
# uri: http://127.0.01:8081 # 路由的目标地址(直接写死)
predicates: # 判断(断言)怎样的请求会被路由
- Path=/users/** # 若请求路径是【/users/】开头的就路由到此
# filters: # 过滤器
# - AddRequestHeader=color, skyblue
- id: orderservice
uri: lb://orderservice
predicates:
- Path=/orders/**
default-filters: # 默认过滤器
- AddRequestHeader=color, green
(3) 全局过滤器 —— GlobalFilter
📝 全局过滤器的作用: 处理一切进入网关的请求和微服务响应,与GatewayFilter 的作用一样
📝 区别:GatewayFilter 通过配置定义,处理逻辑是固定的
📝 GlobalFilter 的逻辑需要自己写代码实现
📚 需求:定义全局过滤器,拦截并判断用户身份
定义全局过滤器,拦截请求。判断请求参数是否满足以下条件:
① 参数中是否有 authorization
② authorization 的参数值是否是 admin
若 ① 和 ② 同时满足,则放行;否则拦截
// @Order(value = 6) // 过滤器执行顺序(值越小,优先级越高)
@Component // 放入 IoC
public class GlobalGatewayFilter implements GlobalFilter , Ordered {
/**
* 拦截并处理全部的请求,处理成功后通过 chain 进入下一个过滤器
*
* @param exchange 请求上下文(可获取 Request、Response 等信息)
* @param chain 放行该请求,进入下一过滤器
* @return 返回,标识当前过滤器结束
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求参数
// 该请求是 WebFlux 中的(与 HttpServletRequest 不同)
ServerHttpRequest request = exchange.getRequest();
// 获取请求参数(键值对)
MultiValueMap<String, String> paramMap = request.getQueryParams();
String paramVal = paramMap.getFirst("authorization");
if (paramVal == null || !paramVal.equals("admin")) {
// 拦截(返回)
ServerHttpResponse response = exchange.getResponse();
// 未登录状态码
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete(); // 返回
}
// 放行
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 6;
}
}
(4) 过滤器的执行顺序
📚 请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
📚 请求路由后,会将当前路由过滤器、DefaultFilter、GlobalFilter 合并为一个过滤器链(集合)中,然后进行排序。排序之后,依次执行每个过滤器。
📚 每个过滤器都必须指定一个 int 类型的 order 值【order 值越小,优先级越高,执行顺序越靠前】
📚 GlobalFilter 通过实现 Ordered 接口或添加 @Order
注解来指定 order 值
📚 路由过滤器和 DefaultFilter 的 order 值由 Spring 指定【在各自过滤器内部按照声明顺序 从1 递增】
📚 当过滤器的 order 值一样时,优先级是:DefaultFilter > 路由过滤器 > GlobalFilter
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: userservice
uri: lb://userservice
predicates:
- Path=/users/**
filters: # 路由过滤器
- AddRequestHeader=color, red # order = 1
- AddRequestHeader=color, blue # order = 2
- AddRequestHeader=color, green # order = 3
default-filters: # 默认过滤器
- AddRequestHeader=name, zgq # order = 1
- AddRequestHeader=name, ytr # order = 2
- AddRequestHeader=name, zhn # order = 3
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()
: ① 加载 DefaultFilter;② 加载路由过滤器;③ 合并 DefaultFilter 和路由过滤器org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()
: ① 加载全局过滤器;② 与 DefaultFilter 和路由过滤器合并;③ 根据合并后的 order 值排序;④ 组织过滤器链
六、网关跨域
(1) 跨域
浏览器有个同源策略(Same-Origin Policy):默认情况下,AJAX 请求只能发送给同源的 URL
📚 AJAX:Asynchronous Javascript And XML【异步 JavaScript 和 XML】
- 同源指 ① 协议、② 域名(IP)、③ 端口 相同
- img、script、link、iframe、video、audio 等标签不受同源策略约束
跨域问题: 浏览器要求发送请求的来源和接收请求的接口的协议、域名和端口必须相同,否则该请求会被浏览器拦截
📚 CORS(Cross-Origin Resource Sharing):跨域资源共享
(2) 网关跨域处理
- 要访问
http://localhost:10010/orders/getOrderById/101?authorization=admin
地址的资源 - 发送请求的来源是
http://localhost:8082/#/pages/index/index
❌ 端口不相同(不符合浏览器的同源策略)
📚 跨域访问检查发送的是 OPTIONS 请求
📚 跨域访问检查有效期要合理
spring:
cloud:
gateway: # 网关相关配置
globalcors: # 全局跨域处理
add-to-simple-url-handler-mapping: true # 防止 options 请求被拦截
cors-configurations:
'[/**]': # 拦截全部路径
allowedOrigins: # 允许跨域访问的来源
- "http://localhost:8082"
allowedMethods: # 允许跨域访问的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许跨域访问携带请求头信息
allowCredentials: true # 允许跨域访问携带 cookie
maxAge: 360000 # 本次跨域访问检查的有效期