二. Gateway 网关基础使用示例与自定义全局过滤器

2023-11-13

一. 步骤

  1. 需求,现在有一个服务需要使用 Gateway 网关服务实现转发访问
  2. 创建目标服务,将目标服务注册到注册中心
  3. 此处以 Eureka 为注册中心,需要创建 Eureka 注册中心服务
  4. 创建 Gateway 网关服务,配置网关服务拦截目标服务路由,将网关服务注册到注册中心
  5. 根据需求在网关服务中创建GatewayFilter 过滤器,实现拦截过滤功能

二. 目标服务

  1. 目标服务 yml 配置文件,以指定服务名称,注册到 Eureka 注册中心
server:
  port: 8001 #当前服务端口号

spring:
  application:
    name: cloud-payment-service #当前服务名称
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver #mysql驱动包
    url: jdbc:mysql://localhost:3306/test01?serverTimezone=GMT%2B8 #连接的库
    username: root
    password:
#=================eureka相关配置======================
eureka:
  client:
    register-with-eureka: true #true 表示将当前服务注册到eureka注册中心
    #true 表示是否在注册中心抓取已有的注册信息,集群环境时必须为true,配合ribbon进行负载
    fetchRegistry: true
    service-url:
      #eureka 注册中心访问连接,集群环境多个注册地址
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka
  instance:
    instance-id: payment8001 #配置当前服务向eureka注册中心注册时显示的服务器主机名称
    prefer-ip-address: true #配置在开发人员直接访问eureka服务器时显示当前eureka上注册的服务的ip
    lease-renewal-interval-in-seconds: 1 #指定定时向eureka注册中心发送代表当前服务心跳包的时间默认30秒
    lease-expiration-duration-in-seconds: 2 # Eureka 接收到当前服务最后一次发送代表正常心跳包的等待时间,超过则将当前服务在 Eureka 上踢除
#=================eureka相关配置end======================

mybatis:
  mapperLocations: classpath:mapper/*.xml #扫描mappper.xml */
  type-aliases-package: com.test.dao #扫描对应mapper.xml 接口
  1. 目标服务需要通过 Gateway 网关转发访问的请求
@RestController
public class PaymentController {

    @Autowired
    private PaymentServerApi paymentServerApi;

    @GetMapping(value = "/getRun")
    public JsonResult getRun() {
        System.out.println("执行查询所有数据8001");
        return JsonResult.success();
    }

    @GetMapping(value = "/payment/getById/{id}")
    public JsonResult getById(@PathVariable("id") Long id) {
        return paymentServerApi.getPaymentById(id);
    }
 }
  1. 目标服务启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient //表示当前 PaymentMain8001 服务注册到Eureka中
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

三. 创建 Gateway 网关服务

  1. pom 文件中引入 Gateway 依赖,注意点不可以引入 spring-boot-starter-web 依赖,如果引入了启动会报错
<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. yml 文件配置拦截的目标服务,uri 中配置通过目标服务名称在注册中心获取目标服务调用地址,本地服务实现调用
server:
  port: 9527

spring:
  application:
    name: cloud-gateway #当前服务名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启网关通过注册中心动态创建路由功能,利用微服务进行路由
      routes:
        #分组路由,可以定义多个
        - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001 #转发到的真实服务器地址(该方式是写死的)
          uri: lb://cloud-payment-service #目标服务在注册中心的名称
          
          #配置断言条件(可以简单理解为通过网关转发请求访问上面 uri 配置的服务下的接口,
          #执行下面 predicates 中配置的断言,可以同时配置多个,符合条件则放行
          predicates:
            - Path=/payment/getById/** #Path路径断言,通过网关访问服务时判断访问路径是否是path指定的,"**"表示模糊匹配*/

        #分组路由"payment_route2"
        - id: payment_route2
          #uri: http://localhost:8001
          uri: lb://cloud-payment-service 
          predicates:
            - Path=/getRun
            #After 断言在指定时间以后才可以通过网关访问uri设置的服务上的接口
            #这个时间字符串可以通过 ZonedDateTime获取
            - After=2020-05-01T02:46:36.560+08:00[Asia/Shanghai]
            
          #配置过滤器
          filters:
            #Gateway内置GatewayFilter类型过滤器
            #表示会对接收到的请求添加"X-Request-Foo"请求头,并且值为"Bar"
            - AddRequestHeader=X-Request-Foo, Bar
            
#将当前 Gateway 服务注册到 Eureka注册中心
eureka:
  client:
    register-with-eureka: true #true 表示将当前服务注册到eureka注册中心
    #true 表示是否在注册中心抓取已有的注册信息,集群环境时必须为true,配合ribbon进行负载
    fetchRegistry: true
    service-url:
      #eureka 注册中心访问连接,集群环境多个注册地址
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka
  instance:
    instance-id: gateway9527 #配置当前服务向eureka注册中心注册时显示的服务器主机名称
    prefer-ip-address: true #配置在开发人员直接访问eureka服务器时显示当前eureka上注册的服务的ip
    lease-renewal-interval-in-seconds: 1 #指定定时向eureka注册中心发送代表当前服务心跳包的时间默认30秒
    # Eureka 接收到当前服务最后一次发送代表正常心跳包的等待时间,超过则将当前服务在 Eureka 上踢除
    lease-expiration-duration-in-seconds: 2 
    hostname: cloud-gateway-service
#=================eureka相关配置end======================

一些其它的 predicates 断言与 filters 示例,如果需求中用到可以配置在yml 中

predicates:
	#Before 断言在指定时间以前才可以通过网关访问uri设置的服务上的接口
	- Before=2020-05-01T02:46:36.560+08:00[Asia/Shanghai]
	 #Between 指定时间范围,在该范围内才允许访问
	 - Between=2020-05-01T02:46:36.560+08:00[Asia/Shanghai],2021-05-01T02:46:36.560+08:00[Asia/Shanghai]
	
	 #Cookie 断言,匹配是否携带了key为username,值是否与"aaa"匹配
	 - Cookie=username,aaa
	 #Header 请求头断言,匹配请求头中是携带 X-Request-Id 数据,值是否与"\d+"正则匹配
	 - Header=X-Request-Id,\d+
	 #Host 断言,匹配访问时的host是否与指定的正则匹配
	 - Host=**.com
	 #Method 断言,匹配是否是指定的请求方式
	 - Method=GET
	 #Query 断言,匹配发送的请求是否包含名为 "valName" 的参数,并且值要与后面的"\d+"正则匹配
	 - Query=valName,\d+
	
 #配置过滤器
filters:
    #表示对请求添加名为"foo"的参数,值为"bar"
    - AddRequestParameter=foo, bar
    #表示请求执行完毕后添加名为"X-Response-Foo"响应头,值为"Bar"
    - AddResponseHeader=X-Response-Foo, Bar
    #保留原始请求的host头信息,而不是通过Gateway网关转发后的请求头
    - PreserveHostHeader
    #匹配ip
    - RemoteAddr=127.0.0.1
    #路径重写过滤器,假设上面配置的"predicates:- Path=/server-api/getRun",在请求服务时每次都需要携带"/server-api"路径
    #通过此方式,重新路径,假设以前请求服务接口为"当前网关服务ip地址:当前网关服务端口号/server-api/getRun"
    #使用路径重写过滤器后可以直接访问"当前网关服务ip地址:当前网关服务端口号/getRun",会自动在端口号后添加 RewritePath 指定的
    #"/server-api",其中",/$\{segment} "正则表达式表示空
    - RewritePath=/server-api/(?<segment>.*),/$\{segment} 

  1. Gateway 网关服务启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient 
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class,args);
    }
}
  1. 最终效果,此时启动注册中心,启动目标服务,启动 Gateway 网关服务,以前通过 “http://目标服务ip:目标服务端口号/访问路径” 直接访问接口,现在可以 “http://网关服务ip:网关服务端口号/目标服务访问路径” 通过网关服务转发的访问访问到目标服务,会自动通过网关服务中配置的目标服务名称在注册中心获取目标服务访问地址列表,负载访问
  2. 直接访问微服务接口
    在这里插入图片描述
  3. 通过网关间接访问服务接口
    在这里插入图片描述

三. 自定义 GatewayFilter 过滤器

  1. 增加需求,在发起请求访问目标服务的时候判断请求中是否携带了名为 “name” 的参数,如果没有则拒绝访问,在以前不使用网关服务的时候可以通过自定义 jvm 层面的过滤器拦截判断,每个单体目标服务中都要创建该过滤器,现在可以在网关服务中创建网关过滤器,通过网关过滤器统一对请求过滤
  2. 在网关服务中创建自定义过滤器,需要继承 GlobalFilter 与 Ordered 接口,重写 GlobalFilter 中的 filter() 方法,为过滤方法,方法中 return GatewayFilterChain 调用 filter() 表示当前过滤器拦截放行,执行下一个过滤,如果在该return 以前执行了非 GatewayFilterChain 调用 filter() 的return 表示拒绝访问
  3. 重写 Ordered 中的 getOrder() 方法,设置当前过滤器的访问优先级,返回 int 变量数值越小优先级越高
  4. 代码示例
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {

    //1.过滤方法,在方法中当 GatewayFilterChain 调用 filter() 时代表过滤放行,执行下一个过滤
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("自定过滤器执行");
        //1.通过 ServerWebExchange 获取 Request,拿到用户发送请求时的所有信息
        ServerHttpRequest request = exchange.getRequest();
        //获取请求头
        request.getHeaders();
        //获取uri
        request.getURI();
        //获取请求体
        request.getBody();

        //2.此处以获取请求携带的参数
        MultiValueMap<String, String> valueMap =  request.getQueryParams();
        //判断是否存有名字为"name"的参数
        String nameVal = valueMap.getFirst("name");

        if(null == nameVal){
            System.out.println("过滤拦击返回执行");
            //3.通过 ServerWebExchange 获取 Response,设置响应前台的数据
            ServerHttpResponse response = exchange.getResponse();
            //此处以没有"name"值时响应前台不被接受的响应码
            response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            //4.响应前台拒绝访问
            return response.setComplete();
        }

        //5.当 GatewayFilterChain 调用 filter() 方法时代表拦截放行向下执行下一个过滤器
        return chain.filter(exchange);
    }

    //2.优先级,通过该方法返回的int值来设置当前过滤器加载执行的优先级,越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
  1. 此时的效果,当用户通过网关服务间接的请求目标服务时,会自动执行网关服务中创建的这个自定义过滤器,获取请求中的参数,判断参数中是否携带了名为"name"的变量,如果为空则拒绝访问并相应前台拒绝访问的状态吗,如果不为空拦截放行执行下一个过滤器

四. 通过自定义 GatewayFilter 过滤器实现允许跨域问题

  1. 在Spring Boot 中如果设置一个请求允许跨域,可以通过一个 @CrossOrigin 注解修饰,但是每个请求上都要添加该注解
@RestController
@CrossOrigin //表示该类下的接口支持跨域请求
public class PaymentController {

    @Autowired
    private PaymentServerApi paymentServerApi;

    @GetMapping(value = "/getRun")
    public JsonResult getRun() {
        System.out.println("执行查询所有数据8001");
        return JsonResult.success();
    }
}
  1. 方式二: 通过自定义 GatewayFilter 定义过滤器,拦截请求,统一设置请求允许跨域
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
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;

@Component
public class CrossGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"POST,GET,PUT,DELETE");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,"*");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二. Gateway 网关基础使用示例与自定义全局过滤器 的相关文章

随机推荐

  • 弯道超车,阿里高工新产Java面试速成指南,面试骚操作都在里面了

    金三银四过去一半 市场火热 但是大家就业压力却没有缓解多少 很多粉丝后台留言 Java程序员面临的竞争太激烈了 我自己也有实感 多年身处一线互联网公司 虽没有直面过求职跳槽的残酷 但经常担任技术面试考官 对程序员招聘市场的现状很清楚 导致现
  • C++中.cpp和.hpp的区别

    时间 2020 06 02 标签 c cpp hpp 区别 栏目 C C 繁體版 原文 https my oschina net u 4398200 blog 3336507 原文地址 https blog csdn net qzx9059
  • H5中写一个下拉框,点击下拉框内容会出现在文本域中

    朋友出去面试做的面试题 分享了下 我就拿来做做 原题 HTML中有个下拉框 包含 风 雨 雷 电 添加事件 当选择风时 文本域内出现选择
  • 使用proteus仿真集成运放构成的三角波发生电路

    仿真电路图 仿真示波器结果
  • 用python做小游戏——以弹球游戏为例

    本博客使用了Pygame库来创建游戏窗口和处理游戏逻辑 一 代码详细解释 首先 通过调用pygame init 来初始化Pygame库 然后创建一个窗口并设置标题 pygame init screen pygame display set
  • 当全景视频遇到快手

    收到 山巅之处 正午的阳光透过翻滚的云海铺洒在岩石壁上 镶嵌在天边连绵起伏的山峦若隐若现 雄伟壮观 俯瞰去 脚下是天梯般陡峭的山崖 连接着山中绿洲 纵身一跃 映入眼帘的是逐渐清晰的林海 那些拔地而起的树木紧密相连 铸成一座郁郁葱葱的 绿色长
  • docker容器内服务访问宿主机服务

    我的个人博客 逐步前行STEP 本文背景 操作系统 macOs 笔者的docker虚拟机中运行了nginx node服务用来部署一个前后端分离的网站 但是由于docker内的node服务运行效率极低 每次代码更新后也不会自动重新编译 所以准
  • 函数间隔和几何间隔

    问题描述 求一个任意点 到一个超平面的距离 超平面表示 在线性代数中 一个超平面可以用下式表示 y X W T
  • 借助ChatGPT自动生成PPT

    借助ChatGPT自动生成PPT 首先让GPT生成一段markdown格式的PPT内容 尽量描述全面 以什么语言 什么格式 排版等等 打开mindshow网址 点击import and create 选择以markdown方式创建 再次点击
  • c++: 实战详解vector

    目的 本文从实际使用的角度出发 简介C 中vector的基本用法 如增 删 改 查等 并举例说明 增 如下代码演示如何向vector中添加元素 其中 include
  • NotePad++添加到右键快捷方式

    首先看效果图 NotePad 添加到右键快捷方式 接下来是操作方式 首先在桌面上新建一个txt文本文档 然后将写入如下内容 Windows Registry Editor Version 5 00 HKEY CLASSES ROOT she
  • 人才梯队如何搭建,3个维度让你打造一支人才团队

    模型在手 方法我有 文末完整版 一 人才梯队可以不建立吗 二 人才梯队建设的目的 三 梯队人才的培养模型 四 梯队人才的管理 五 人才梯队建设发展通道 人才盘点 素质模型 人才盘点的内容 人才盘点的结果 人才分类 人才选拔 晋升与发展 今日
  • hive中常见的谓词操作符/比较符号

    hive中
  • STM32F103移植FreeRTOS必须搞明白的系列知识---2(FreeRTOS任务优先级)

    STM32F103移植FreeRTOS必须搞明白的系列知识 1 Cortex CM3中断优先级 STM32F103移植FreeRTOS必须搞明白的系列知识 2 FreeRTOS任务优先级 STM32F103移植FreeRTOS必须搞明白的系
  • 一文带你了解芯片制造的6个关键步骤

    在智能手机等众多数码产品的更新迭代中 科技的改变悄然发生 苹果A15仿生芯片等尖端芯片正使得更多革新技术成为可能 这些芯片是如何被制造出来的 其中又有哪些关键步骤呢 智能手机 个人电脑 游戏机这类现代数码产品的强大性能已无需赘言 而这些强大
  • el-radio值无法回显

    首先检查 label 绑定一个动态变量 其次label为number类型可以直接回显 为string类型需要在最外层再加一层单引号
  • 【Android Socket专题】:UDP通信客户端app的demo的实现

    Android Socket 专题 UDP Client客户端 http blog csdn net shankezh article details 50731287 UDP Server服务器 http blog csdn net sh
  • Linux里隐藏的计算器,你知道它的奥秘吗?

    大家都知道 windows下有个计算器工具 我们在工作生活中经常使用到它 但是 你可知Linux下也同样有个计算器吗 当然 良许说的是命令行下的计算器工具 而不是界面型的计算器 良许是Linux应用开发工程师 平时基本是在命令行下工作 所以
  • MYSQL数据类型

    MySQL中定义数据字段的类型对你数据库的优化是非常重要的 MySQL支持多种类型 大致可以分为三类 数值 日期 时间和字符串 字符 类型 数值类型 MySQL支持所有标准SQL数值数据类型 这些类型包括严格数值数据类型 INTEGER S
  • 二. Gateway 网关基础使用示例与自定义全局过滤器

    目录 一 步骤 二 目标服务 三 创建 Gateway 网关服务 三 自定义 GatewayFilter 过滤器 四 通过自定义 GatewayFilter 过滤器实现允许跨域问题 一 步骤 需求 现在有一个服务需要使用 Gateway 网