gateway应用(1)

news2024/11/24 17:45:14

1 简介

简单理解---业务服务的统一入口,方便实现,服务路由,安全,限流,过滤,黑白名单,证书加密解密,服务降级/熔断,灰度,等等

2 介绍

  1. Predicate(断言):如果请求路径与断言相匹配则进行路由。(路径匹配是常见断言)
  2. Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
  3. Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,路由成功,则可以执行pre Filter  ,服务响应回数据,可以执行 Post Filter。

3 注意事项

3.1

spring-boot-starter-parent和spring-cloud-starter-gateway版本要一致,否则可能报错

4 基本路由实战

4.1 pom文件

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gateway</groupId>
    <artifactId>gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2.1.3.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>${spring-cloud.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${spring-cloud.version}</version>
        </dependency>
    </dependencies>

 配置文件

server:
  port: 80
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
     # config:
     #   server-addr: localhost:8848 # Nacos 服务器地址
    gateway:
      routes:
        - id: goods-server #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://goods-server      #匹配后提供服务的路由地址,lb负载
          predicates:
            - Path=/goods/**     # 断言,路径相匹配的进行路由
        - id: order-server
          uri: lb://order-server
          predicates:
            - Path=/order/**
            #- Path=/goods/,/goods/** 多个可以逗号隔开
      enabled: true #开启网关,默认true 如果程序引用了spring-cloud-starter-gateway,但不希望启用网关 设置为false

4.2 测试路由转发

另外一个 goods 服务,直接访问 http://localhost:8080/goods/good/list 即可出现数据

 直接访问gateway服务 http://localhost/goods/good/list 自己就会转发到goods服务上

4.3 StripPrefix

有一种场景,前端会在所有的接口前加一个api。前端请求的路径是 http://localhost/api/goods/good/list  。实际上请求 http://localhost/goods/good/list 怎么办

StripPrefix=1的意思就是去掉路径上第一个,也就是api将会去掉。 当我们访问。http://localhost/api/goods/good/list  它会自动变成  http://localhost/goods/good/list

5 限流

  添加 pom坐标

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

注意点:

配置文件中 redis-rate-limiter.replenishRate ,redis-rate-limiter.burstCapacity等同于配置bean中的 RedisRateLimiter,但是同时存在时,只有配置文件的配置生效。

server:
  port: 80
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
     # config:
     #   server-addr: localhost:8848 # Nacos 服务器地址
    gateway:
      routes:
        - id: goods-server #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://goods-server      #匹配后提供服务的路由地址
          predicates:
            - Path=/goods/**     # 断言,路径相匹配的进行路由
          filters:
            - name: RequestRateLimiter
              args:
                #spel表达式 它会去容器查找名称为hostKeyResolver 的Bean。
                key-resolver: '#{@hostKeyResolver}'
                #针对同一个key,允许的每秒请求数,不包括被抛弃的请求。这实际是令牌桶填充率。
                redis-rate-limiter.replenishRate: 1
                #针对同一个key,一秒内允许的最大请求数。这实际是令牌桶可容纳的最大令牌数。若设为0,则拒绝所有请求。
                redis-rate-limiter.burstCapacity: 1
package com.order.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * @创建人 赵伟
 * @创建时间 2024/3/18
 * @描述
 */
@Slf4j
@Configuration
public class RouteConfig {
/*    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getQueryParams().getFirst("userId")
            //exchange.getRequest().getHeaders().getFirst("X-Forwarded-For") 基于请求ip的限流
        );
    }*/
/*    *//**
     * 根据 路径 进行限流
     *
     * @return KeyResolver
     *//*
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getPath()).toString());
    }*/
    /**
     * 根据 HostAddress 进行限流
     *
     * @return KeyResolver
     */
    @Bean
    public KeyResolver hostKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostString());
    }
    /**
     * Redis 令牌桶 限流
     *   这个等同于配置文件中的 redis-rate-limiter.replenishRate,redis-rate-limiter.burstCapacity
     *   同时存在时,加载的是配置文件中的配置
     * @return RedisRateLimiter
     */
    @Bean RedisRateLimiter redisRateLimiter() {
        return new RedisRateLimiter(1, 1);
    }
}

6黑白名单

/**
 * @描述 禁用ip
 */
public class BlackIpFilter implements GlobalFilter, Ordered {
    @Override
    public int getOrder() {
        return 0;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String remoteIp = GatewayUtil.getRemoteIp(request);
        //查询 ip接口-缓存等实现
        if (true) {
            //返回禁用提示码
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
}

7 权限或token/续时/传递用户信息/串行/

package com.order.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.order.entity.MyUser;
import com.order.utils.GatewayUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
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.bind.annotation.RequestMethod;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @创建人 赵伟
 * @创建时间 2024/3/20
 * @描述 禁用ip
 */
@Component //需要将实现设置为Spring的组件
public class BlackIpFilter implements GlobalFilter, Ordered {
    /**
     * 顺序执行
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //1 ip黑名单
        String remoteIp = GatewayUtil.getRemoteIp(request);
        //查询 ip接口-缓存等实现
        if (false) {
            //返回禁用提示码
            return loseToken(exchange,response);
          //  return exchange.getResponse().setComplete();
        }

        //2 OPTIONS 不允许
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            //返回禁用提示码
            exchange.getResponse().setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
            return exchange.getResponse().setComplete();
        }



        //4 忽略的url
        String pass = "/aaa/getToken|/aaa/updatePwd";
        String uri = request.getURI().toString();
        if (isPass(pass, uri)) {
            return chain.filter(exchange);
        }

        //5 校验token
        List<String> tokenList = request.getHeaders().get("token");
        if (CollectionUtil.isEmpty(tokenList)) {
            return loseToken(exchange,response);
        }

        //6 根据token 获取redis用户信息判断是否失效,
        //注意多端系统生成token规则
        String token = tokenList.get(0);
        //自己写逻辑
        MyUser user = queryCacheByToken(token);
        if (ObjectUtil.isNull(user)) {
            return loseToken(exchange,response);
        }

        //7 已经获取到用户了,判断用户状态,是够禁用/用户密码强制多少天修改。 登录接口也要实现,
        boolean forbidden = false;//禁用
        if (forbidden) {
            return loseToken(exchange,response);
        }
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
        //put是覆盖
      //  queryParams.put("userId", Collections.singletonList("123"));
        // add则不会覆盖,而是继续添加
     //   queryParams.add("userId","123");

        //3 设置请求头加串行id
        ServerHttpRequest.Builder mutate = request.mutate();
        //不存在新增,存在即修改
        mutate.header("serialId", UUID.randomUUID().toString());


        //8 赋值用户信息 可用jwt加密 子服务 通过拦截器 在解析,放到ThreadLocal
         //请求头中文乱码
      //  mutate.header("userInfo", String.valueOf(user));
        String s = Base64.getEncoder().encodeToString(String.valueOf(user).getBytes());
        mutate.header("userInfo", s );
        ServerHttpRequest build = mutate.build();
        exchange.mutate().request(build).build();

        //9 token续时
        //刷新token失效时间 是否每次请求都要续时,还是先获取判断小于一定时间在续时
      //  redisTemplate.setExpire(token, 60 * 60 * 24);

        return chain.filter(exchange);
    }

    /**
     * 假的获取缓存用户信息
     * @param token
     * @return
     */
    private MyUser queryCacheByToken(String token) {
        MyUser myUser = new MyUser();
        myUser.setUserId("123");
        myUser.setUserName("小明");
        myUser.setUpdatePasswordTime(new Date());
        return myUser;
    }

    /**
     * 失效token
     * @param exchange
     * @param response1
     * @return
     */
    private Mono<Void> loseToken(ServerWebExchange exchange,ServerHttpResponse response1) {
        // 设置响应的Content-Type头并指定编码为UTF-8
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        JSONObject responseBody = new JSONObject();
        responseBody.put("result", Boolean.FALSE);
        responseBody.put("msg", "您当前登录状态已失效,请重新登录");
        responseBody.put("status", 400);
        responseBody.put("isSuccess",  Boolean.FALSE);
        String s = JSON.toJSONString(responseBody);
        byte[] bytes = new String(s.getBytes(), StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
        return  exchange.getResponse().writeWith(Flux.just(buffer));


/*
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code","401");
        jsonObject.put("message","非法请求");
        byte[] datas = JSON.toJSONString(jsonObject).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(datas);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));*/


    }

    /**
     * 是否放行的url
     * @param passString
     * @param requestURI
     * @return
     */
    public static boolean isPass(String passString, String requestURI) {
        if (StringUtils.isNotBlank(passString)) {
            String[] split1 = passString.split("\\|");
            for (int i = 0; i < split1.length; i++) {
                String s = split1[i];
                if (!StringUtils.isBlank(s) && StringUtils.containsIgnoreCase(requestURI, s)) {
                    return true;
                }
            }
        }
        return false;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Raven:一款功能强大的CICD安全分析工具

关于Raven Raven是一款功能强大的CI/CD安全分析工具&#xff0c;该工具旨在帮助广大研究人员对GitHub Actions CI工作流执行大规模安全扫描&#xff0c;并将发现的数据解析并存储到Neo4j数据库中。 Raven&#xff0c;全称为Risk Analysis and Vulnerability Enumeration for C…

Linux操作系统基础入门​

1、操作系统简介​ a. DOS 和 Windows NT​ DOS是一个与Unix完全不相干的操作系统&#xff0c;这一点可以从DOS使用反斜杠来表示文件目录分隔符上 看出来。随着IBM个人PC的流行&#xff0c;微软DOS操作系统在个人电脑上流行起来。Windows 95, Windows 98, 和 Windows ME底层都…

Databend 集成 PRQL:现代数据处理的一小步

PRQL&#xff0c;读作 “Prequel”&#xff0c;是一种与 SQL 并肩的查询语言&#xff0c;它的独到之处在于采用了管道式的语法&#xff0c;在查询关系数据库时显得更加直观和高效。 Databend 拥抱 PRQL 在 v1.2.380-nightly 版本中&#xff0c;得益于社区贡献者 ncuwaln 提交…

《PDVC》论文笔记

PS&#xff1a;模型代码解释清明后出 原文链接&#xff1a; [2108.07781v1] End-to-End Dense Video Captioning with Parallel Decoding (arxiv.org) 原文笔记&#xff1a; What&#xff1a; End-to-End Dense Video Captioning with Parallel Decoding 并行解码的端到端…

法律行业案例法模型出现,OPenAI公布与法律AI公司Harvey合作案例

Harvey与OpenAl合作&#xff0c;为法律专业人士构建了一个定制训练的案例法模型。该模型是具有复杂推理广泛领域知识以及超越单一模型调用能力的任务的AI系统&#xff0c;如起草法律文件、回答复杂诉讼场景问题以及识别数百份合同之间的重大差异。 Harvey公司由具有反垄断和证…

uniapp,文字超出几行显示省略号...,展开显示更多

效果图&#xff1a; 代码&#xff1a; <template><view class"text-container"><text class"text-content" click"showDetail">{{ text }}</text><text v-if"showMore" class"view-detail" cli…

ModusToolbox 实战入门- XMC GPIO应用篇

导读 ModusToolbox™ 软体&#xff1a;MCU 开发的利器 ModusToolbox™ 软体是一组支援 MCU 周边配置和应用的工具和发展。这些工具使您能够将我们的 MCU 整合到您现有的开发方法中。 ModusToolbox™ 软体的优点 提供完整的 MCU 周边配置和应用工具可整合到现有的开发方法中…

C++——异常机制

目录 一&#xff0c;背景 1.1 C语言处理错误的方式 1.2 C异常概念 二&#xff0c;异常的使用 2.1 异常的简单使用 2.2 异常的匹配原则 2.3 异常抛对象 2.4 异常的重新抛出 2.5 异常安全 三&#xff0c;自定义异常体系 四&#xff0c;异常优缺点 4.1 优点 4.2 缺点 …

NOIP2014提高组D1T2:联合权值

题目链接 NOIP2014提高组D1T2&#xff1a;联合权值 题目描述 无向连通图 G G G 有 n n n 个点&#xff0c; n − 1 n-1 n−1 条边。点从 1 1 1 到 n n n 依次编号,编号为 i i i 的点的权值为 W i W_i Wi​&#xff0c;每条边的长度均为 1 1 1。图上两点 ( u , v ) (…

环保用电监测系统诞生与作用

随着全球能源危机的加剧和环境保护意识的提高&#xff0c;环保用电监测系统应运而生。这一系统以其独特的监测能力、数据分析和节能减排功能&#xff0c;在提高用电效率和促进环境可持续发展方面发挥着重要作用。本文将从环保用电监测系统的诞生背景、主要功能、作用以及在实际…

基于Springboot + MySQL + Vue 大学新生宿舍管理系统 (含源码)

目录 &#x1f4da; 前言 &#x1f4d1;摘要 &#x1f4d1;操作流程 &#x1f4da; 系统架构设计 &#x1f4da; 数据库设计 &#x1f4ac; 管理员信息属性 &#x1f4ac; 学生信息实体属性 &#x1f4ac; 宿舍安排信息实体属性 &#x1f4ac; 卫生检查信息实体属性 &…

leet hot 100-10 和为 K 的子数组

和为 K 的子数组 原题链接思路代码 原题链接 leet hot 100-10 560. 和为 K 的子数组 思路 看到连续非空数组 想到前缀和数组 首先记录前缀和 然后从前往后运算 计算当前位置的前缀和的大小 减少k个 那么这个数字在前缀和的数组中有多少 时间复杂度O(n) 空间复杂度(n) 代…

python基础——模块【模块的介绍,模块的导入,自定义模块,*和__all__,__name__和__main__】

&#x1f4dd;前言&#xff1a; 这篇文章主要讲解一下python基础中的关于模块的导入&#xff1a; 1&#xff0c;模块的介绍 2&#xff0c;模块的导入方式 3&#xff0c;自定义模块 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C语言入门基…

招聘信息分享(第一期)

今天给大家带来——测绘、地信、遥感领域的事业单位招聘信息&#xff01;这也是我自己在关注的&#xff0c;自己应聘单位大多时间已经截至&#xff0c;后期会陆续分享&#xff0c;先分享近期招聘的事业单位 文章目录 1、宁夏大学2024年人才招聘2、甘肃有色冶金职业技术学院3、…

【现代企业管理】企业组织结构和组织文化的理论与实践——以华为为例

一、前言 管理是科学和艺术的统一体&#xff0c;它是企业成长的保证。企业管理中&#xff0c;管理者面对的往往不是一个完整的系统&#xff0c;而是各种不具有整体规律性的零碎信息的总和&#xff0c;因此进行信息的整合和研究是管理的重点和关键。 组织管理作为管理的四大职…

【QingHub】QingHub Studio企业级应用作业编排

简介 QingHub作业编排中心是一个通过插件化方式&#xff0c;提供数据从采集&#xff0c;转化&#xff0c;计算&#xff0c;存储为一体的全流程数据处理方案&#xff0c;他一方面为前端应用提供数据源&#xff0c;同时也为前端应用与数据源头的通信搭建起桥梁&#xff0c;实现数…

链表之单链表

上一篇博客我们学习了线性表中的顺序表&#xff0c;这一篇博客让我们继续往下了解线性表的链表&#xff0c;链表分为好几种结构&#xff0c;活不多说&#xff0c;让我们开始学习吧&#xff01; 目录 1.链表 2.链表的结构 3.单链表的实现 1.链表 1.概念&#xff1a;它是一种物…

快速跨国传输怎么实现?

在当今全球化的商业舞台上&#xff0c;迅速且安全地跨国界传输大型文件已经成为企业运营的一个核心环节。但是&#xff0c;这一过程往往面临速度缓慢和安全隐患的问题&#xff0c;这些问题严重地影响了企业的工作效率和数据的安全性。小编将会深入探讨企业在进行跨国大文件传输…

揭秘!自定义三维模型如何在RflySim中实现仿真(三)

一.技术背景 揭秘&#xff01;自定义三维模型如何在RflySim中实现仿真&#xff08;一&#xff09; 揭秘&#xff01;自定义三维模型如何在RflySim中实现仿真&#xff08;二&#xff09; 上两篇文章我们学习了自定义三维模型如何在RflySim中实现仿真和三维场景导入RflySim的实…

ssm023实验室耗材管理系统设计与实现+jsp

实验室耗材管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对实验室耗材信息管理混乱&#xff…