【个人技术经验及开发技巧分享】 【个人技术经验及开发技巧分享】
首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载

Louis

首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载
  • 操作系统初识

  • JAVA基础

  • JVM

  • 开发框架

    • SpringMVC
    • Spring
    • Mybatis
    • Sentinel
    • Spring Security Oauth2
    • SpringBoot
    • Spring Cloud Gateway
      • 1 简介
      • 2 快速开始
        • 2.1 环境配置
        • 2.1.1 引入依赖
        • 2.1.2 编写yml配置文件
        • 2.2 路由断言工厂
        • 2.2.1 时间匹配
        • 2.2.2 Cookie匹配
        • 2.2.3 Header匹配
        • 2.2.4 路径匹配
        • 2.2.5 自定义路由断言工厂
        • 2.3 过滤器工厂
        • 2.3.1 添加请求头
        • 2.3.2 添加请求参数
        • 2.3.3 路径添加前缀
        • 2.3.4 重定向
        • 2.3.5 自定义过滤器工厂
        • 2.4 全局过滤器
        • 2.4.1 LoadBalancerClientFilter
        • 2.4.2 自定义全局过滤器
        • 2.5 Gateway跨域配置
        • 2.5.1 yml配置方式
        • 2.5.2 java配置方式
  • Redis

  • Zookeeper

  • 消息中间件

  • 持久化

  • 算法

  • 网络

  • 系统架构

  • 学习笔记
  • 开发框架
luoxiaofeng
2022-08-10
目录

Spring Cloud Gateway

# 1 简介

Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。

不能在传统的 servlet 容器中工作,也不能构建成 war 包。

Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

核心概念

路由(route)

路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。

断言(predicates)

Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。

过滤器(Filter)

SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。

# 2 快速开始

# 2.1 环境配置

# 2.1.1 引入依赖

<!-- gateway网关 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- nacos服务注册与发现 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11

注意

会和spring-webmvc的依赖冲突,需要排除spring-webmvc

# 2.1.2 编写yml配置文件

server:
  port: 8888
spring:
  application:
    name: test-gateway
  #配置nacos注册中心地址
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    gateway:
      discovery:
        locator:
          # 默认为false,设为true开启可以通过微服务名访问服务
          # http://localhost:8888/test-order/order/getbyno/1
          enabled: true
      # 是否开启网关    
      enabled: true 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2.2 路由断言工厂

# 2.2.1 时间匹配

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          predicates:
             # 匹配在指定的日期时间之后发生的请求  入参是ZonedDateTime类型
            - After=2021-01-31T22:22:07.783+08:00[Asia/Shanghai]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

获取ZonedDateTime类型的指定日期时间

ZonedDateTime zonedDateTime = ZonedDateTime.now();//默认时区
// 用指定时区获取当前时间
ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
1
2
3

# 2.2.2 Cookie匹配

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          predicates:
             # Cookie匹配
            - Cookie=username, testname
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.2.3 Header匹配

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          predicates:
             # Header匹配  请求中带有请求头名为 x-request-id,其值与 \d+ 正则表达式匹配
            - Header=X-Request-Id, \d+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.2.4 路径匹配

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          predicates:
             # Path路径匹配
            - Path=/test/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.2.5 自定义路由断言工厂

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。

注意

命名需要以 RoutePredicateFactory 结尾

@Component
@Slf4j
public class CheckAuthRoutePredicateFactory 
        extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

    public CheckAuthRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {

            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                log.info("调用CheckAuthRoutePredicateFactory" + config.getName());
                if(config.getName().equals("testname")){
                    return true;
                }
                return false;
            }
        };
    }

    /**
     * 快捷配置
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("name");
    }

    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          predicates:
             # 自定义CheckAuth断言工厂
            - name: CheckAuth
              args:
                name: testname
      #      - CheckAuth=testname
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.3 过滤器工厂

# 2.3.1 添加请求头

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          filters:
            - AddRequestHeader=X-Request-color, red  #添加请求头
1
2
3
4
5
6
7
8
9
10
11
12
13
14

测试http://localhost:8888/test-order/testgateway

@GetMapping("/testgateway")
public String testGateway(HttpServletRequest request) throws Exception {
    log.info("gateWay获取请求头X-Request-color:"
            +request.getHeader("X-Request-color"));
    return "success";
}
@GetMapping("/testgateway2")
public String test2(@RequestHeader("X-Request-color") String color) throws Exception {
    log.info("gateWay获取请求头X-Request-color:"+color);
    return "success";
}
1
2
3
4
5
6
7
8
9
10
11

# 2.3.2 添加请求参数

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          filters:
            - AddRequestParameter=color, blue  # 添加请求参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14

测试http://localhost:8888/test-order/testgateway3

@GetMapping("/testgateway3")
public String test3(@RequestParam("color") String color) throws Exception {
    log.info("gateWay获取请求参数color:"+color);
    return "success";
}
1
2
3
4
5

# 2.3.3 路径添加前缀

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          filters:
            - PrefixPath=/test-order-abc  # 添加前缀 对应微服务需要配置context-path
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.3.4 重定向

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          filters:
            - RedirectTo=302, https://www.baidu.com/  #重定向到百度
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.3.5 自定义过滤器工厂

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。

@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            log.info("调用CheckAuthGatewayFilterFactory==="
                    + config.getName() + ":" + config.getValue());
            return chain.filter(exchange);
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: http://localhost:8020  #目标微服务的请求地址和端口
          filters:
            - CheckAuth=test,123
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.4 全局过滤器

提示

GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由。

# 2.4.1 LoadBalancerClientFilter

LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个URI),如果该值的scheme是 lb,比如:lb://myservice ,它将会使用Spring Cloud的LoadBalancerClient 来将 myservice 解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。

提示

其实就是用来整合负载均衡器Ribbon的

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 默认为false,设为true开启可以通过微服务名访问服务
      enabled: true # 是否开启网关
      routes:
        - id: test-order # id只要唯一即可,建议配合服务名
          uri: lb://test-order #目标微服务的请求地址和端口
          predicates:
             # Path路径匹配
            - Path=/test/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.4.2 自定义全局过滤器

@Component
@Order(-1)
@Slf4j
public class CheckAuthFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //校验请求头中的token
        List<String> token = exchange.getRequest().getHeaders().get("token");
        log.info("token:"+ token);
        if (token.isEmpty()){
            return null;
        }
        return chain.filter(exchange);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.5 Gateway跨域配置

# 2.5.1 yml配置方式

spring:
  cloud:
    gateway:
        globalcors:
          cors-configurations:
            '[/**]':
              allowedOrigins: "*"
              allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.5.2 java配置方式

@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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class CorsFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (CorsUtils.isCorsRequest(request)) {
            HttpHeaders requestHeaders = request.getHeaders();
            ServerHttpResponse response = exchange.getResponse();
            HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
            HttpHeaders headers = response.getHeaders();
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
            if (requestMethod != null) {
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
            }
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "18000L");
            if (request.getMethod() == HttpMethod.OPTIONS) {
                response.setStatusCode(HttpStatus.OK);
                return Mono.empty();
            }
        }
        return chain.filter(exchange);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SpringBoot
基础

← SpringBoot 基础→

最近更新
01
SpringBoot
10-21
02
Spring
10-20
03
Sentinel
10-14
更多文章>
Copyright © 2022-2023 Louis | 粤ICP备2022060093号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式