统一网关 Gateway【微服务】

文章目录

    • 1. 前言
    • 2. 搭建网关服务
    • 3. 路由断言工厂
    • 4. 路由过滤器
      • 4.1 普通过滤器
      • 4.2 全局过滤器
      • 4.3 过滤器执行顺序
    • 5. 跨域问题处理

1. 前言

通过前面的学习我们知道,通过 Feign 就可以向指定的微服务发起 http 请求,完成远程调用。但是这里有一个问题,我的微服务好像谁来都可以访问,这样会不会不安全?那是肯定的。
实际生产中,有很多的业务模块只有我们内部的人才能用,不是谁都能访问,这时候我们就需要对用户的身份进行验证,而这个验证的工作就是网关(Gateway)来完成的。

一切请求一定要先到网关,再到微服务,其实是对微服务的一种保护措施。

(1)网关功能:

① 身份认证和权限校验(认证通过后,再将请求转发到对应的微服务)
② 服务路由(根据请求信息判断应该将请求转发到哪个微服务)
③ 负载均衡(对外的服务也会有多个实例,需要做负载均衡)
④ 请求限流(限制微服务的用户请求量)

注意 Ribbon 是对内部的 userserver 做负载均衡,而网关是对外部的 orderserver 做负载均衡!

(2)网关的技术实现:

① gataway(新技术,响应式编程,非阻塞式,性能好,吞吐能力强)
② zuul(较早的技术,基于 Servlet,阻塞式,性能差)

2. 搭建网关服务

① 创建新的 module,引入 SpringCloudGateway 的依赖和 Nacos 的服务发现依赖

<dependencies>
    <!--nacos服务注册发现依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--网关gateway依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

在这里插入图片描述

② 创建启动类

在这里插入图片描述

在这里插入图片描述

③ 编写路由配置及 Nacos 地址,路由配置由三部分组成:路由 id、目标地址和路由断言(路由断言其实就是匹配规则)

路由断言:判断请求是否符合要求,符合则转发到路由目的地。

server:
  port: 10010 #网关端口
spring:
  application:
    name: gateway #服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos地址
    gateway:
      routes: #网关路由配置
        - id: user-server #路由id,自定义,只要唯一即可
          uri: lb://userserver #路由的目标地址,lb就是负载均衡,后面跟服务名
          predicates: #路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** #只要以/user/开头就符合要求
        - id: order-server
          uri: lb://orderserver
          predicates:
            - Path=/order/**

在这里插入图片描述

路由不止一个,用 - 来表示路由数组!

④ 依赖冲突报错

如果你的 spring-boot-starter-web 依赖是写到父工程里的,那么不出意外的话启动 GatewayApplication 时将会报错:

在这里插入图片描述
翻译过来就是 SpringMVC 的依赖冲突了,SpringCloudGateway 的内部是通过 netty+webflux 实现的,而 webflux 实现和 SpringMVC 的配置依赖是有冲突的。

解决办法有两种:
第一种就是去除父工程中的 spring-boot-starter-web 这个依赖, 然后在各个子工程需要的地方进行单独引入依赖即可;
第二种就是在 pom.xml 中进行修改,用以下依赖替换原来的 spring cloud gateway。

<!--网关gateway依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </exclusion>
    </exclusions>
</dependency>

⑤ 启动项目测试一下,访问 http://localhost:10010/order/2

在这里插入图片描述

在这里插入图片描述

整体流程:
用户发起 http://localhost:10010/user/2 请求,接着该请求进入网关,而网关是无法处理具体业务的,所以它会基于路由规则去做判断,然后代理到具体的服务上去处理业务。
上面步骤中我们定义了两个路由规则,以 user 开头的路径将会被代理到 userserver 服务,接着网关会拿着服务名(userserver)去找 Nacos 拉取服务列表,然后做一个负载均衡,发送请求 http://localhost:10010/user/2。

网关只是负责检验并转发请求,而最终处理业务的是具体的服务!

3. 路由断言工厂

我们在配置文件中写的断言规则只是字符串,这些字符串会被断言工厂读取并处理,最终转变为路由判断的条件。

Spring 提供了 11 种基本的 Predicate 工厂:

在这里插入图片描述

Path 是我们用的最多的方式!

测试一下,时间设在 2031 年之后才可以访问,所以现在肯定是不能访问的:

只有当断言规则都满足的时候才能访问成功,路径符合,但时间不符合,照样不能访问。

在这里插入图片描述

在这里插入图片描述

4. 路由过滤器

4.1 普通过滤器

GatewayFilter 是网关中提供的一种过滤器,可以处理进入网关的请求,以及微服务返回的响应。

路由之后并不是立即向微服务发起请求的,其实我们还可以给路由配置各种各样的过滤器,这些过滤器最终会形成一个过滤器链,对进入网关的请求做各种处理。
请求给了微服务,微服务处理完之后会返回一个结果,这个结果也是先到达网关,网关同样会通过过滤器逐层处理这个响应结果,最终返回给用户。

断言匹配成功相当于请求通过了网关的大门,然后过滤器会对请求做一些处理(比如添加请求头、请求参数等),最后网关会将处理好的请求代理到具体的服务,做业务处理。

在这里插入图片描述

Spring 提供了 31 种不同的路由过滤器工厂:

在这里插入图片描述

做一个测试,我们可以在 Controller 中获取一下请求头,并在控制台输出:

在这里插入图片描述
在这里插入图片描述

如果要对所有的路由都生效,则可以将过滤器工厂写到 default-filters 中,与 routes 处于同一级:

在这里插入图片描述

4.2 全局过滤器

全局过滤器即自定义过滤器,全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。区别在于 GatewayFilter 通过配置定义,处理逻辑是固定的,而 GlobalFilter 的逻辑需要自己写代码实现, 定义方式是实现 GlobalFilter 接口。

做一个案例,定义全局过滤器拦截请求,判断请求的参数是否满足下面两个条件:
① 参数中是否有 authorization;
② authorization 的参数值是否为 admin。
如果同时满足则放行,否则拦截。

① 创建 AuthorizeFilter 类,实现 GlobalFilter 接口,并重写 filter 方法

在这里插入图片描述

在这里插入图片描述

第一个参数用于处理业务逻辑,第二个参数是用来放行的,把请求委托给下一个过滤器去处理,其实就是调用下一个过滤器的 filter 方法,等同于放行。

② 编写核心代码

过滤器之间是有先后执行顺序的,这个我们可以通过注解的方式设置,也可以通过实现 Ordered 接口的方式来设置,数值越小,优先级越高!

package com.zxe.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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;

//定义过滤器执行的顺序,数值越小优先级越高
//可以通过注解设置,也可以通过实现Ordered接口来设置
//@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
//    exchange请求上下文,里面可以获取Request、Response等信息
//    chain用来把请求委托给下一个过滤器
//    return表示当前过滤器业务结束
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        //2.获取参数中的authorization参数
        String auth = params.getFirst("authorization");
        //3.判断参数是否等于admin
        if ("admin".equals(auth)) {
            //4.等于admin放行,其实是调用下一个过滤器的filter方法,等同于放行
            return chain.filter(exchange);
        }
        //5.不等于就拦截,后面的业务不会再继续,我们一般会设置状态码返回给用户,提示未登录
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    //定义过滤器执行的顺序,数值越小优先级越高
    @Override
    public int getOrder() {
        return -1;
    }
}

在这里插入图片描述

③ 重启项目,我们可以在请求路径中携带 authorization 参数测试一下,无 authorization 参数或参数值不正确的一律被拦截,只有当 authorization = admin 时才会被放行

在这里插入图片描述

在这里插入图片描述

整体流程:
① 用户请求进入网关,断言匹配失败,报 404 错误,匹配成功直接将请求送入过滤器链;
② 过滤器分为普通过滤器和自定义过滤器,普通过滤器可以在配置文件中设置,自定义过滤器需要实现 GlobalFilter 接口,它可以处理更复杂的过滤业务;
③ 过滤不通过的请求就会被拦截,报401错误,只有层层过滤都通过了,请求才会被路由到具体的服务上,处理具体的业务。

4.3 过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由过滤器、默认过滤器和全局过滤器。

查看当前路由过滤器和默认过滤器的源码知道,它们的过滤器工厂首先会读取配置文件,然后生成一个真正的过滤器 GatewayFilter。再看全局过滤器的源码,可以看到 GlobalFilter 其实是被 GatewayFilterAdapter 适配到 GatewayFilter 的,也就是说网关中所有的过滤器都是 GatewayFilter 类型的。既然是同一种类型,那么就可以把它们放到同一个集合里面去排序。

所以请求路由后,会将当前路由过滤器和默认过滤器、全局过滤器合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。

每个过滤器都必须指定一个 int 类型的 order 值,order 值越小,优先级越高,执行顺序就越靠前。

GlobalFilter 通过实现 Ordered 接口,或者添加 @Order 注解来指定 order 值,过滤器的执行顺序由我们自己指定。而当前路由过滤器和默认过滤器的执行顺序是由 Spring 指定的,默认按照声明顺序从 1 开始递增,这里需要注意的是当前路由过滤器和默认过滤器是分开计数的,这就会出现计数冲突的问题(比如都是 1)。
源码里面的默认过滤器是先加载的,然后再加载当前路由过滤器,最后加载全局过滤器,组织过滤器链。所以,当过滤器的 order 值一样时,会按照 默认过滤器 > 当前路由过滤器 > 全局过滤器的顺序执行。

5. 跨域问题处理

域名不一致就是跨域,主要包括:
① 域名不同:www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
② 域名相同,端口不同。localhost:8080 和 localhost:8081

跨域问题:浏览器禁止请求的发起者与服务端发生跨域 ajax 请求,请求被浏览器拦截。

网关处理跨域采用的是 CORS 方案,CORS的底层逻辑网关其实已经帮我们做好了,我们只需要简单的配置即可实现。

spring:
  cloud:
    gateway:
      globalcors: #全局的跨域处理
        add-to-simple-url-handler-mapping: true #解决options请求被拦截的问题
        cors-configurations:
          '[/**]':
            allowedOrigins: #允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.leyou.com"
            allowedMethods: #允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" #允许在请求中携带的头信息
            allowCredentials: true #是否允许携带cookie
            maxAge: 360000 #这次跨域检测的有效期

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA】线程的run()和start()有什么区别?

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 run() 方法&#xff1a; start() 方法&#xff1a; 区别总结&#xff1a; 结语 我的其他博客 前言 多线程编程是Java中一个重要…

生成式 AI 如何重塑软件开发流程和开发工具?

生成式AI正在重塑开发流程和开发工具&#xff0c;通过自动化和优化软件开发过程&#xff0c;提高开发效率和质量。它可以帮助开发人员快速生成代码、测试和部署应用程序&#xff0c;同时减少错误和缺陷。此外&#xff0c;生成式AI还可以帮助开发人员快速理解和解决复杂的技术问…

Vulnhub靶机:Corrosion1

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;corrosion:1&#xff08;10.0.2.12&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/c…

Vue3-46-Pinia-获取全局状态变量的方式

使用说明 在 Pinia 中&#xff0c;获取状态变量的方式非常的简单 &#xff1a; 就和使用对象一样。 使用思路 &#xff1a; 1、导入Store&#xff1b;2、声明Store对象&#xff1b;3、使用对象。 在逻辑代码中使用 但是 Option Store 和 Setup Store 两种方式定义的全局状态变量…

0109作业

1> 思维导图 2> 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin&quo…

设计与实现基于Java+MySQL的模拟银行ATM操作系统

课题背景 随着现代经济的发展&#xff0c;电子支付和自动化银行服务已成为人们生活中不可或缺的一部分。自动取款机&#xff08;ATM&#xff09;作为一种常见的自助服务设备&#xff0c;使用户能够方便地进行资金的存取、查询余额、转账等操作&#xff0c;而无需到银行柜台。 …

浅淡A100-4090-性价比

大模型的训练用 4090 是不行的&#xff0c;但推理&#xff08;inference/serving&#xff09;用 4090 不仅可行&#xff0c;在性价比上还能比 H100 稍高。4090 如果极致优化&#xff0c;性价比甚至可以达到 H100 的 2 倍。 -------------------- FP64&#xff08;双精度浮点&a…

pycharm社区版配置flask开发环境

新建配置文件&#xff0c;类型选择Shell Script 设置Execute中flask.exe的路径 设置options &#xff1a;--appflask_app.py run --port5000 --debug 设置working 路径 设置环境变量FLASK_APPflask_app.py;FLASK_ENVdevelopment 注意&#xff1a;FLASK_APPflask_app.py和上…

automa插件使用的一些经验

automa&#xff0c;我承认我写不出来这样的代码&#xff0c;早年的时候公司想过做一个爬虫的工具&#xff0c;那个时候RPA还没有火&#xff0c;虽然下载也没怎么火.RPA再牛&#xff0c;还是需要工程师&#xff0c;想一点经验都没有人来做&#xff0c;还是理解不了。能够简化数据…

Java接口的解析

在 Java 中&#xff0c;接口&#xff08;Interface&#xff09;是一种抽象类型&#xff0c;用于定义一组相关方法的契约。接口只包含方法的签名&#xff0c;而没有方法的实现。实现接口的类必须提供接口中定义的方法的具体实现。 以下是对 Java 接口的解析&#xff1a; 这只是…

【uniapp】遇到的一些问题

一、小程序中textarea ios样式不生效的方法 默认有内边距&#xff0c;加个disable-default-padding"true" 二、uni-data-picker循环使用&#xff0c;一个改了全局的值 换成了uni自带的picker&#xff0c;下面括号里必须有默认值&#xff0c;为空字符串的时候&…

国产AI工具钉钉AI助理:开启个性化助手服务的新篇章

钉钉AI助理是钉钉平台的一项功能&#xff0c;它可以根据用户的需求提供个性化的AI助手服务。用户可以在AI助理页面一键创建个性化的AI助理&#xff0c;如个人的工作AI助理、旅游AI助理、资讯AI助理等。企业也可以充分使用企业所沉淀的知识库和业务数据&#xff0c;在获得授权后…

Python实现PDF—>Excel的自动批量转换(附完整代码)

Python实现PDF—>Excel的自动批量转换&#xff08;附完整代码&#xff09; 话不多说&#xff0c;先看效果&#xff01; 需要转换的PDF&#xff1a; 转换后的Excel&#xff1a; 01、底层原理 PDF 到 Excel 的转换涉及不同文件格式之间的数据提取和重构。底层原理可以简…

【GDAL】Windows下VS+GDAL开发环境搭建

Step.0 环境说明&#xff08;vs版本&#xff0c;CMake版本&#xff09; 本地的IDE环境是vs2022&#xff0c;安装的CMake版本是3.25.1。 Step.1 下载GDAL和依赖的组件 编译gdal之前需要安装gdal依赖的组件&#xff0c;gdal所依赖的组件可以在官网文档找到&#xff0c;可以根据…

白嫖aws创建Joplin server服务器

网上有很多的Joplin服务器的搭建教程&#xff0c;但是基本都是抄来抄去&#xff0c;对初学者实在是太不友好了。 话不多说&#xff0c;说干就干&#xff0c;自己从头找资料搭了一个&#xff0c;这可能是全网最好的Joplin服务器搭建教程了。 aws服务器 aws的服务器还是很香的&…

idea git回滚之前提交记录

提交代码时&#xff0c;如果不小心提交了不需要提交的内容&#xff0c;在本地仓库中&#xff0c;此时需要回滚版本&#xff0c;如何回滚 1.打开git控制台&#xff0c;左下角git,选择要处理的分支&#xff0c;选择刷新获取最新git提交记录 2&#xff09;选中自己commit需要回滚…

Fluids —— Minimal fluid setups

目录 Waterline FLIP Boundary Boundary flow 创建流体设置的三个基本方法&#xff1b; Waterline 由FLIP Container SOP与FLIP Solver SOP组成的基本network&#xff0c;可不需要任何外部源&#xff1b; FLIP Container SOP&#xff0c;能使用不同的容器形状&#xff1b;F…

PiflowX-MysqlCdc组件

MysqlCdc组件 组件说明 MySQL CDC连接器允许从MySQL数据库读取快照数据和增量数据。 计算引擎 flink 组件分组 cdc 端口 Inport&#xff1a;默认端口 outport&#xff1a;默认端口 组件属性 名称展示名称默认值允许值是否必填描述例子hostnameHostname“”无是MySQL…

MySql -数据库基本概念

一、数据库的基本概念 1.为什么要学数据库&#xff1f; 之前我们如果想将一些数据实现永久化存储&#xff0c;可以怎么做呢&#xff1f;没错。使用IO流的技术将数据保存到本地文件中但是接下来我有这样一个需求&#xff1a;将下面的user.txt文件中的王五年龄修改为35 张三 2…

leetcode 动态规划(最后一块石头的重量II、目标和、一和零)

1049.最后一块石头的重量II 力扣题目链接(opens new window) 题目难度&#xff1a;中等 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < …