引入gateway的依赖:spring-cloud-starter-gateway不需要spring-boot-starter-web;如果他们同时存在则gateway不可用。故我们需要把父工程中的spring-boot-starter-web排除,其它子模块重新引入web包即可。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-demoartifactId>
<groupId>com.acxgroupId>
<version>1.0.0version>
parent>
<modelVersion>4.0.0modelVersion>
<groupId>com.acxgroupId>
<artifactId>gateway-serviceartifactId>
<version>1.0.0version>
<packaging>jarpackaging>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
project>
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
gateway:
routes:
- id: order-service # 自定义路由id,必须唯一
uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
predicates:
- Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
- id: product-service # 商品服务路由id
uri: lb://product-service
predicates:
- Path=/product/**,/product/api/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**,/user/api/**
作用:读取断言规则并处理,转变为路由判断的条件,最后规则由PathRoutePredicateFactory断言工厂处理。
11种断言工厂:
断言工厂 | 说明 |
---|---|
After | 某个时间点之后请求 |
Before | 某个时间点之前请求 |
Between | 某两个时间点之间请求 |
Cookie | 请求必须包含某些cookie |
Header | 请求必须包含某些header |
Host | 请求必须是指定方式 |
Method | 请求必须是指定方法:GET、POST |
Path(默认) | 请求路径必须是指定路由规则 |
Query | 请求必须包含指定参数 |
RemoteAddr | 请求这IP必须为指定范围 |
Weight | 权重处理 |
11种断言示例:具体的示例可以查询官网文档https://www.springcloud.cc/spring-cloud-greenwich.html#gateway-request-predicates-factories
# After
predicates:
- After=2022-01-20T17:42:47.789-07:00[America/Denver]
# Before
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
# Between
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
# Cookie
predicates:
- Cookie=chocolate, ch.p # 使用Cookie路由断言工厂,配置cookie,正则表达式(可有可无)
# Header
predicates:
- Header=X-Request-Id, \d+
# Host
predicates:
- Host=**.somehost.org,**.anotherhost.org
# Method
predicates:
- Method=GET,POST
# Query
predicates:
- Query=green
# RemoteAddr
predicates:
- RemoteAddr=192.168.1.1/24
# Weight
predicates:
- Weight=group1, 2
Spring Cloud Gateway官网实例:https://www.springcloud.cc/spring-cloud-greenwich.html#_addrequestheader_gatewayfilter_factory
常用的过滤器工厂:
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand 的名称 |
已AddRequestHeader拦截器为例
网关服务做如下配置:
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
gateway:
routes:
- id: order-service # 自定义路由id,必须唯一
uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
predicates:
- Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
filters:
- AddRequestHeader=abc,hello word # 给order服务配置拦截器
- id: product-service # 商品服务路由id
uri: lb://product-service
predicates:
- Path=/product/**,/product/api/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**,/user/api/**
Order服务添加获取abc请求头代码:
package com.acx.controller;
import com.acx.client.UserClient;
import com.acx.pojo.vo.ActorInfoVO;
import com.acx.pojo.vo.OrderInfoVO;
import com.acx.pojo.vo.StudentVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("order")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
@Autowired
private RestTemplate restTemplate;
@Autowired
private UserClient userClient;
@Autowired
private StudentVO studentVO;
@GetMapping("getStudent")
public StudentVO getStudent() {
return studentVO;
}
@GetMapping("getOne")
public OrderInfoVO getOne(@RequestHeader("abc") String abc) {
logger.info("开始查询订单");
OrderInfoVO orderInfoVO = new OrderInfoVO();
orderInfoVO.setOrderName("订单123");
orderInfoVO.setOrderSn("046b399937ad4271bcd5ed275f2b4682");
orderInfoVO.setProductName("商品123");
orderInfoVO.setProductNum(23);
int userId = 1;
// String getUserUrl = "http://127.0.0.1:8083/user/getUser/" + userId;
// String getUserUrl = "http://user-service/user/getUser/" + userId;
//服务发现
// ActorInfoVO actor = restTemplate.getForObject(getUserUrl, ActorInfoVO.class);
ActorInfoVO actor = userClient.getUser(userId);
orderInfoVO.setUser(actor);
logger.info("测试AddRequestHeader拦截器是否生效,abc={}", abc);
return orderInfoVO;
}
}
请求Order接口测试:http://localhost:10010/order/getOne
2022-05-09 22:41:58.698 INFO 19452 --- [nio-8081-exec-1] com.acx.controller.OrderController : 测试AddRequestHeader拦截器是否生效,abc=hello word
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
gateway:
routes:
- id: order-service # 自定义路由id,必须唯一
uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
predicates:
- Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
- id: product-service # 商品服务路由id
uri: lb://product-service
predicates:
- Path=/product/**,/product/api/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**,/user/api/**
default-filters:
- AddRequestHeader=abc,hello word # 全局配置拦截器
作用:处理一切进入网关服务的请求和响应,与GatewayFilter作用一样,区别在于GlobalFilter支持自定义逻辑扩展。
核心方法:
public interface GlobalFilter {
/**
* Process the Web request and (optionally) delegate to the next {@code WebFilter}
* through the given {@link GatewayFilterChain}.
* @param exchange the current server exchange
* exchange: 请求上下文,里面包含了Request、Response等信息
* @param chain provides a way to delegate to the next filter
* chain: 用来把请求委托给下一个过滤器
* @return {@code Mono} to indicate when request processing is complete
* MonoZ: 返回时表示当前过滤器逻辑流程结束
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
Gateway网关服务:自定义认证拦截器,此拦截器集成GlobalFilter
package com.acx.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Order(1) //拦截器优先级;值越小优先级越高
@Component
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取request
ServerHttpRequest request = exchange.getRequest();
//2.获取请求参数
MultiValueMap<String, String> params = request.getQueryParams();
//3.获取token
String token = params.getFirst("token");
if ("login".equals(token)) {
//4.是、请求通过
return chain.filter(exchange);
}
//5.否、校验未通过
//5.1.设置校验未通过code 401 认证未通过
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
测试结果:认证失败
测试结果:认证成功
前言:请求到达网关服务后,一共要经过路由过滤器、DefaultFilter、GlobalFilter三种过滤器组成的过滤器链。他们会有一个执行顺序,排序后一次执行每个过滤器。
规则:
Order值:
实例如下:
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
gateway:
routes:
- id: order-service # 自定义路由id,必须唯一
uri: lb://order-service # lb是负载均衡(默认是轮询)的意思,后面跟的是服务名称
predicates:
- Path=/order/**,/order/api/** # 这是路由断言;只要是以/orderservice或者/orderservice/api开头的请求就会被路由到order-service服务上
filters:
- AddRequestHeader=zhangsan,abc123 # order = 1,越往后Order值就越大
- id: product-service # 商品服务路由id
uri: lb://product-service
predicates:
- Path=/product/**,/product/api/**
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**,/user/api/**
default-filters:
- AddRequestHeader=abc,hello word # 全局配置拦截器;order=1,越往后Order值就越大
浏览器禁止请求发送者与服务端发生卡与Ajax请求;请求域名或者端口与后台目标域名地址不一样时就会出现这种问题。
例子:下面的情况就会出现cors跨域问题
spring:
cloud:
gateway:
# 允许跨域请求配置
globalcors:
cors-configurations:
'[/**]':
# 允许任何域名使用
allowedOrigins: "*"
# 允许任何头
allowedHeaders: "*"
# 允许任何方法(post、get等)
allowedMethods: "*"
# sessionid 多次访问一致
allowCredentials: true
# 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求
add-to-simple-url-handler-mapping: true # 允许来自所有域名(allowedOrigins)的所有请求方式(allowedMethods)发出CORS请求7
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许任何域名使用
corsConfiguration.addAllowedOrigin("*");
// 允许任何头
corsConfiguration.addAllowedHeader("*");
// 允许任何方法(post、get等)
corsConfiguration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}