spring cloud gateway (五)

news2024/12/24 2:12:27

Gateway简介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安
全,监控和限流。
优点:
1 性能强劲:是第一代网关Zuul的1.6倍
2 功能强大:内置了很多实用的功能,例如转发、监控、限流等
3 设计优雅,容易扩展
缺点:
1 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
2 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
3 需要Spring Boot 2.0及以上的版本,才支持
在这里插入图片描述

Gateway

 		<!-- SpringCloud Alibaba Sentinel Gateway -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
spring:
  redis:
    host: localhost
    port: 6379
    password:
  cloud:
    gateway:
      discovery:
        locator:
          lowerCaseServiceId: true
          enabled: true # 让gateway可以发现nacos中的微服务
      routes:# 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务
        # 认证中心
        - id: com-xzx-auth # 当前路由的标识, 要求唯一
          #lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://com-xzx-auth  # 请求要转发到的地址
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/service-auth/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters: # 当请求路径满足Path指定的规则时,才进行路由转发
            - StripPrefix=1 # 转发之前去掉1层路径

        # 商品中心
        - id: com-xzx-shop
          uri: lb://com-xzx-shop 
          predicates:
            - Path=/service-shop/**
          filters:
            - StripPrefix=1
        # 工单
        - id: com-xzx-order
          uri: lb://com-xzx-order
          predicates:
            - Path=/service-order/**
          filters:
            - StripPrefix=1
        # 用户中心
        - id: com-xzx-user
          uri: lb://com-xzx-user
          predicates:
            - Path=/service-user/**
          filters:
            - LogGatewayFilterFactory=true,true
            - StripPrefix=1
# 安全配置
security:
  # 不校验白名单
  ignore:
    whites:
      - /service-auth/auth/logout
      - /service-auth/auth/login
      - /service-auth/auth/login/test
      - /service-auth/auth/register

Gateway 内置有许多的路由断言

基于Datetime类型的断言工厂

此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]

基于远程地址的断言工厂 RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中

-RemoteAddr=192.168.1.1/24

基于Cookie的断言工厂

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.

基于Header的断言工厂

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否
具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+

基于Host的断言工厂

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org

基于Method请求方法的断言工厂

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET

基于Path请求路径的断言工厂

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}

基于Query请求参数的断言工厂

QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具
有给定名称且值与正则表达式匹配。
-Query=baz, ba.

基于路由权重的断言工厂

WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9

在这里插入图片描述

gateway自定义断言

//泛型 用于接收一个配置类,配置类用于接收中配置文件中的配置
    @Component
    public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
        public AgeRoutePredicateFactory() {
            super(AgeRoutePredicateFactory.Config.class);
        }
        //用于从配置文件中获取参数值赋值到配置类中的属性上
        @Override
        public List<String> shortcutFieldOrder() {
            //这里的顺序要跟配置文件中的参数顺序一致
            return Arrays.asList("minAge", "maxAge");
        }
        //断言
        @Override
        public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config
                                                          config) {
            return new Predicate<ServerWebExchange>() {
                @Override
                public boolean test(ServerWebExchange serverWebExchange) {
                    String ageStr =
                            serverWebExchange.getRequest().getQueryParams().getFirst("age");
                    if (StringUtils.isNotEmpty(ageStr)) {
                        int age = Integer.parseInt(ageStr);
                        return age > config.getMinAge() && age < config.getMaxAge();
                    }
                    return true;
                }
            };
        }
    }
    //自定义一个配置类, 用于接收配置文件中的参数
    @Data
    class Config {
        private int minAge;
        private int maxAge;
    }
# 自定义断言使用
routes:
- id: product-route
uri: lb://service-product
predicates:
- Path=/product-serv/**
- Age=18,60  # 限制年龄只有在18到60岁之间的人能访问
filters:
- StripPrefix=1

gateway 过滤器

gateway 过滤器分局部过滤器和全局过滤器,局部过滤要配置在具体的路由下,全局过滤器则对所有路由生效 全局过滤器可用来进行网关鉴权

/**
	全局过滤器
 * 网关鉴权
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
	//网关放行白名单
    private IgnoreWhiteProperties whiteProperties;
    private RedisService redisService;

    @Autowired
    public AuthGlobalFilter(RedisService redisService,IgnoreWhiteProperties whiteProperties){
        this.redisService = redisService;
        this.whiteProperties = whiteProperties;
    }


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpRequest.Builder mutate = serverHttpRequest.mutate();

        String url = serverHttpRequest.getURI().getPath();
        //跳过不需要验证的路径
        if (match(url,whiteProperties.getWhites())){
            return chain.filter(exchange);
        }
        String token = getToken(serverHttpRequest);
        if (StringUtils.isEmpty(token)){
            return unauthorizedResponse(exchange,"令牌不能为空");
        }
        Claims claims = JWTUtils.parseToken(token);
        if (Objects.isNull(claims)){
            return unauthorizedResponse(exchange,"令牌过期或者不正确");
        }
        String userKey = JWTUtils.getUserKey(claims);
        Boolean isLogin = redisService.hasKey(getTokenKey(userKey));
        if (!isLogin){
            return unauthorizedResponse(exchange,"登陆状态已过期");
        }
        String userId = JWTUtils.getUserId(claims);
        String username = JWTUtils.getUsername(claims);
        if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(username)){
            return unauthorizedResponse(exchange,"令牌验证失败");
        }
        addHeader(mutate,"user_key",userKey);
        addHeader(mutate,"user_id",userId);
        addHeader(mutate,"username",username);

        //内部请求来源参数清除
        removerHeader(mutate,"from-source");
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    private void removerHeader(ServerHttpRequest.Builder mutate, String name) {
        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }

    private void addHeader(ServerHttpRequest.Builder mutate, String key, Object value) {
        if (Objects.isNull(value)){
            return;
        }
        String valueString = value.toString();
        String valueEncode = ServletUtils.urlEncode(valueString);
        mutate.header(key,valueEncode);
    }

    private String getTokenKey(String userKey) {
        return "login_tokens:" + userKey;
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径{}",exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(),msg,401);
    }

    private String getToken(ServerHttpRequest serverHttpRequest) {
        String token = serverHttpRequest.getHeaders().getFirst("Authorization");
        if (StringUtils.isNotEmpty(token) && token.startsWith("Bearer")){
            token = token.replaceFirst("Bearer","");
        }
        return token;
    }

    @Override
    public int getOrder() {
        return 0;
    }


    private Boolean match(String url,List<String> whiteList){
        if (StringUtils.isEmpty(url) || CollectionUtils.isEmpty(whiteList)){
            return false;
        }
        for (String whiteUrl : whiteList) {
            if (isMatch(url,whiteUrl)){
                return true;
            }
        }
        return false;
    }

    private Boolean isMatch(String url,String whiteUrl){
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(url,whiteUrl);
    }
}

局部过滤器

@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    @Override
    public String name() {
    	//重写name方法 这里就是配置中的名字,如果不重写 配置中就写Gateway前的 也就是log
        return "LogGatewayFilterFactory";
    }

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

    /**
     * 读取配置文件中的参数 赋值到 配置类中
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
    	// 会把配置中的值赋值到 Config中
        return Arrays.asList("consoleLog","cacheLog");
    }

    @Override
    public GatewayFilter apply(LogGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            if (config.isCacheLog()){
                System.out.println("缓存日志开启");
            }
            if (config.isConsoleLog()){
                System.out.println("控制台日志开启");
            }
            return chain.filter(exchange);
        };
    }

	//接收配置中的参数
    public static class Config{
        private boolean consoleLog;
        private boolean cacheLog;

        public boolean isConsoleLog() {
            return consoleLog;
        }

        public void setConsoleLog(boolean consoleLog) {
            this.consoleLog = consoleLog;
        }

        public boolean isCacheLog() {
            return cacheLog;
        }

        public void setCacheLog(boolean cacheLog) {
            this.cacheLog = cacheLog;
        }
    }
}
## 配置
filters:
  - LogGatewayFilterFactory=true,true
  - StripPrefix=1

gateway 统一异常处理

/**
 * 网关统一异常处理
 */
@Configuration
@Order(-1)
public class GatewayExceptionHandle implements ErrorWebExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandle.class);

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        if (exchange.getResponse().isCommitted()){
            //响应已经提交到客户端
            return Mono.error(ex);
        }
        String msg;
        if (ex instanceof NotFoundException){
            msg = "服务未找到";
        }else if (ex instanceof ResponseStatusException){
            ResponseStatusException responseStatusException = (ResponseStatusException) ex;
            msg = responseStatusException.getMessage();
        }else {
            msg = "内部服务器错误";
        }
        log.error("[网关异常处理]请求路径:{},异常信息:{}",exchange.getRequest().getPath(),ex.getMessage());
        return ServletUtils.webFluxResponseWriter(response,msg);
    }
}

gateway依赖WebFlux所以要学习下WebFlux
WebFlux

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

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

相关文章

java 字典

java 字典 数据结构总览 Map Map 描述的是一种映射关系&#xff0c;一个 key 对应一个 value&#xff0c;可以添加&#xff0c;删除&#xff0c;修改和获取 key/value&#xff0c;util 提供了多种 Map HashMap: hash 表实现的 map&#xff0c;插入删除查找性能都是 O(1)&…

MySQL跨服务器数据映射

MySQL跨服务器数据映射环境准备1. 首先是要查看数据库的federated引擎 开启/关闭 状态2. 打开任务管理器&#xff0c;并重启mysql服务3. 再次查看FEDERATED引擎状态&#xff0c;引擎已启动映射实现问题总结在日常的开发中经常进行跨数据库进行查询数据。 同服务器下跨数据库进…

【SpringCloud】SpringCloud详解之Feign实战

目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.使用Feign远程调用(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽…

STM32之 串口

串口通信串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&#xff0c;只…

Vue基础入门讲义(三)-指令

文章目录1.什么是指令&#xff1f;2.插值表达式2.1.花括号2.2.插值闪烁2.3.v-text和v-html3.v-model4.v-on4.1.基本用法4.2.事件修饰5.v-for5.1.遍历数组5.2.数组角标5.3.遍历对象6.key7.v-if和v-show7.1.基本使用7.2.与v-for结合7.3.v-else7.4.v-show8.v-bind8.1. 属性上使用v…

服务器处理发生异常:java.text.ParseException: Unparseable date

测试上传报文的时候遇见报错 服务器处理发生异常:java.text.ParseException: Unparseable date: “2023/03/03” 错误报文 实际需要的报文 错误原因 上传时间字段&#xff0c;与Date字段数据位数不匹配&#xff0c;Java类型&#xff1a;Date默认带有年月日 时分秒yyyy-mm-dd…

十年业务开发总结,如何做好高效高质量的价值交付

作者&#xff1a;杨博林 阿里大淘宝场景金融团队 软件交付是一个非常复杂的过程和体系&#xff0c;需要保障好每个阶段的质量和效率才能保障最终的质量和效率。本文将尝试从需求交付的前、中、后三个环节来阐述一下如何做高效高质量的价值交付。 一、背景 转眼间已经做了十年的…

JavaScript基础内容

日升时奋斗&#xff0c;日落时自省 目录 1、基础语法 2、DOM 2.1、选中页面元素 2.2、获取/修改元素内容 3、JS案例 3.1、网页版本猜数字 3.2、网页版表白墙 JS最初只是为了进行前端页面的开发后来JS也被赋予了更多的功能&#xff0c;可以用来开发桌面程序&#xff0c;手…

RHCSA-重置root密码(3.3)

方法1&#xff1a;rd.break &#xff08;1&#xff09;首先重启系统&#xff0c;在此页面按e键&#xff0c;在屏幕上显示内核启动参数 &#xff08;2&#xff09;知道linux这行&#xff0c;末尾空格后输入rd.break&#xff0c;然后按ctrlx &#xff08;3&#xff09;查看&#…

电脑桌面上的图标不见了怎么办?5个完美的解决技巧

案例&#xff1a;电脑桌面不显示任何东西&#xff1f; “救命&#xff01;电脑打开后&#xff0c;只有桌面&#xff0c;任何图标都没有怎么办&#xff1f;心急&#xff0c;不知道该怎么解决&#xff1f;” 电脑桌面上的图标消失是一个比较常见的问题&#xff0c;许多用户都会…

Hadoop集群启动从节点没有DataNode

目录 一、问题背景 二、解决思路 三、解决办法&#xff1a; 一、问题背景 之前启动hadoop集群的时候都没有问题&#xff0c;今天启动hadoop集群的时候&#xff0c;从节点的DataNode没有启动起来。 二、解决思路 遇见节点起不来的情况&#xff0c;可以去看看当前节点的日志…

各大加密算法对比(原理、性能、安全、运用)

原理按加密可逆可以分为&#xff1a;加密可逆算法和加密不可逆算法。加密可逆算法又可以分为&#xff1a;对称加密和非对称加密。1、加密不可逆算法&#xff1a;一般采用hash算法加密&#xff0c;其原理一般是将原文长度补位成64的倍数&#xff0c;接着初始化固定长度的缓存值&…

大数据框架之Hadoop:MapReduce(五)Yarn资源调度器

Apache YARN (Yet Another Resource Negotiator) 是 hadoop 2.0 引入的集群资源管理系统。用户可以将各种服务框架部署在 YARN 上&#xff0c;由 YARN 进行统一地管理和资源分配。 简言之&#xff0c;Yarn是一个资源调度平台&#xff0c;负责为运算程序提供服务器运算资源&…

Windows Cannot Initialize Data Bindings 问题的解决方法

前言 拿到一个调试程序, 怎么折腾都打不开, 在客户那边, 尝试了几个系统版本, 发现Windows 10 21H2 版本可以正常运行。 尝试 系统篇 系统结果公司电脑 Windows 8有问题…下载安装 Windows10 22H2问题依旧下载安装 Windows10 21H2问题依旧家里的 笔记本Window 11正常 网上…

第三章 opengl之着色器

OpenGL着色器GLSLGLSL的数据类型向量输入与输出Uniform更多属性自己的着色器类着色器 着色器是运行在GPU上的小程序。着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序&#xff0c;因为它们之间不能相互通信&#xff1b;它们之间唯一的沟通只有通过输入和…

DC-DC模块电源隔离直流升压高压稳压输出5v12v24v转60v100v110v150v220v250v300v400v500v

特点效率高达80%以上1*1英寸标准封装单电压输出稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上应用HRB 0.2~10W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、9~18V、及18~36VDC标准&#…

软工第二次个人作业——软件案例分析

软工第二次个人作业——软件案例分析 项目内容这个作业属于哪个课程2023北航敏捷软件工程这个作业的要求在哪里个人作业-软件案例分析我在这个课程的目标是体验敏捷开发过程&#xff0c;掌握一些开发技能&#xff0c;为进一步发展作铺垫这个作业在哪个具体方面帮助我实现目标通…

四川大学软件学院|系统级编程期末复习

概述 选择题 50 分&#xff08;原题率 80%&#xff09;&#xff1a;http://tieba.baidu.com/p/1250021454?&share9105&frsharewise&unique967707B1DAECEF4A785B61D29AF36950&st1639102957&client_type1&client_version12.10.1&sfccopy&share…

使用C语言实现简单的PNG图像读取

概述 首先&#xff0c;关于png图像的结构&#xff1a;PNG文件的结构、PNG格式的数据结构。这两篇文章说的比较细。我简单地说一下我使用到的地方&#xff1a; 注&#xff1a;①引于PNG格式的数据结构。②引于PNG文件的结构 “png文件的前8个字节为固定的文件头信息&#xff0…

Ubuntu中安装StaMPS

Ubuntu中安装StaMPS0 StaMPS简介1 首先安装好MATLAB&#xff0c;安装一些依赖工具包2 安装StaMPS2.1 下载StaMPS安装包2.2 安装2.3 配置环境2.4 matlab中的路径设置0 StaMPS简介 官网&#xff1a;https://homepages.see.leeds.ac.uk/~earahoo/stamps/ A software package to e…