搭建gateway网关

1.创建springBoot项目

可以将Server URL换成start.aliyun.com 

 

 

 2.配置路由与跨域处理

路由:

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

跨域处理:

 

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

3.自定义过滤器

package cn.itcast.gateway.filters;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();// 2.获取authorization参数String auth = params.getFirst("authorization");// 3.校验if ("admin".equals(auth)) {// 放行return chain.filter(exchange);}// 4.拦截// 4.1.禁止访问,设置状态码exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 4.2.结束处理return exchange.getResponse().setComplete();}
}

过滤器的执行顺序:

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:

排序的规则是什么呢?

每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

 

4.实现定义过滤器中的业务逻辑

//首先配置一个全局异常处理器来处理异常

梳理业务逻辑

@Order(-1)
@Component
@Slf4j
public class AynuFilter implements GlobalFilter {public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志//3.黑白名单//4.用户鉴权//5.请求的模拟接口是否存在//6.请求转发,调用模拟接口//7.响应日志//8.调用成功,接口调用次数+1//9.调用失败,返回一个规范的错误码return exchange.getResponse().setComplete();}
}

 配置请求日志

exchange(路由交换机):我们所有的请求的信息、响应的信息、响应体、请求体都能从这里拿到。

chain(责任链模式):因为我们的所有过滤器是按照从上到下的顺序依次执行,形成了一个链条。所以这里用了一个 chain ,如果当前过滤器对请求进行了过滤后发现可以放行,就要调用责任链中的 next 方法,相当于直接找到下一个过滤器,这里称为 filter 。有时候我们需要在责任链中使用 next,而在这里它使用了filter 来找到下一个过滤器,从而正常地放行请求。

@Order(-1)
@Component
@Slf4j
public class AynuFilter implements GlobalFilter {public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志//3.黑白名单//4.用户鉴权//5.请求的模拟接口是否存在//6.请求转发,调用模拟接口//7.响应日志//8.调用成功,接口调用次数+1//9.调用失败,返回一个规范的错误码return chain.filter(exchange);}
}

 使用exchange获取request,并输出日志

@Order(-1)
@Component
@Slf4j
public class AynuFilter implements GlobalFilter {public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志ServerHttpRequest request = exchange.getRequest();log.info("请求唯一标识:{}",request.getId());log.info("请求路径:{}",request.getPath().value());log.info("请求方法:{}",request.getMethod());log.info("请求参数:{}",request.getQueryParams());log.info("请求来源地址:{}",request.getRemoteAddress());String hostString = request.getLocalAddress().getHostName();log.info("请求来源地址:{}",hostString);//3.黑白名单//4.用户鉴权//5.请求的模拟接口是否存在//6.请求转发,调用模拟接口//7.响应日志//8.调用成功,接口调用次数+1//9.调用失败,返回一个规范的错误码return chain.filter(exchange);}
}

 配置白名单

通常情况下,G经常使用的是封禁IP。例如,如果某个远程地址频繁访问,我们可以将其添加到黑名单并拒绝访问。现在我们来试试设置一个规则,如果请求的来源地址不是 127.0.0.1,就拒绝它的访问。先写一个全局的常量。在这里我们用一个白名单,通常建议在权限管理中尽量使用白名单,少用黑名单。白名单的原则是只允许特定的调用,这样可能会更加安全,或者你可以默认情况下全禁止。

@Order(-1)
@Component
@Slf4j
public class AynuFilter implements GlobalFilter {private static  final List<String> IP_WHITE_LIST = Arrays.asList("127.0.0.1");public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志ServerHttpRequest request = exchange.getRequest();log.info("请求唯一标识:{}",request.getId());log.info("请求路径:{}",request.getPath().value());log.info("请求方法:{}",request.getMethod());log.info("请求参数:{}",request.getQueryParams());log.info("请求来源地址:{}",request.getRemoteAddress());String hostString = request.getLocalAddress().getHostName();log.info("请求来源地址:{}",hostString);//3.黑白名单//获取响应对象ServerHttpResponse response = exchange.getResponse();if (!IP_WHITE_LIST.contains(hostString)){//设置响应状态码为403(禁止访问)response.setStatusCode(HttpStatus.FORBIDDEN);}//4.用户鉴权//5.请求的模拟接口是否存在//6.请求转发,调用模拟接口//7.响应日志//8.调用成功,接口调用次数+1//9.调用失败,返回一个规范的错误码return chain.filter(exchange);}
}

 用户鉴权

这里举个例子,具体实现请参考自己的业务需求

@Order(-1)
@Component
@Slf4j
public class AynuFilter implements GlobalFilter {private static  final List<String> IP_WHITE_LIST = Arrays.asList("127.0.0.1");@AutowiredRedisTemplate redisTemplate;@AutowiredUserMapper userMapper;public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志ServerHttpRequest request = exchange.getRequest();log.info("请求唯一标识:{}",request.getId());log.info("请求路径:{}",request.getPath().value());log.info("请求方法:{}",request.getMethod());log.info("请求参数:{}",request.getQueryParams());log.info("请求来源地址:{}",request.getRemoteAddress());String hostString = request.getLocalAddress().getHostString();log.info("请求来源地址:{}",hostString);//3.黑白名单//获取响应对象ServerHttpResponse response = exchange.getResponse();if (!IP_WHITE_LIST.contains(hostString)){//设置响应状态码为403(禁止访问)response.setStatusCode(HttpStatus.FORBIDDEN);return response.setComplete();}//4.用户鉴权HttpHeaders headers = request.getHeaders();//调用者传过来的参数String accessKey = headers.getFirst("accessKey");String body = headers.getFirst("body");String timestamp = headers.getFirst("timestamp");String random = headers.getFirst("random");String sign = headers.getFirst("sign");//验证随机数,使用redis存储,以sign为键名//操作字符串数据对象ValueOperations valueOperations = redisTemplate.opsForValue();//getString randomDB = (String) valueOperations.get(sign);if (randomDB==null){//setex    TimeUnit是一个枚举类,里面列举了时间单位valueOperations.set(sign,random,120, TimeUnit.HOURS);}else {if (!randomDB.equals(random)){throw new RuntimeException("无权限");}}//调用mapper校验keyUser userDB = userMapper.getUserByAccessKey(accessKey);if (userDB==null){throw new RuntimeException("无权限");}//时间戳验证,时间戳不能和当前时间超过5分钟if (TimeUtils.checkTimesTamp(timestamp)){throw new RuntimeException("无权限");}//秘钥验证,使用传过来的数据生成sign,查询与用户的sign是否一致HashMap<String, String> hashMap = new HashMap<>();hashMap.put("accessKey",accessKey);hashMap.put("body",body);hashMap.put("random", random);hashMap.put("timestamp",timestamp);String newSign = SignUtils.getSign(hashMap, userDB.getSecretKey());if (!newSign.equals(sign)){throw new RuntimeException("无权限");}//5.请求的模拟接口是否存在//todo//6.请求转发,调用模拟接口//7.响应日志//8.调用成功,接口调用次数+1//9.调用失败,返回一个规范的错误码return chain.filter(exchange);}
}

 自定义响应处理

问题:
预期是等模拟接口调用完成,才记录响应日志、统计调用次数。

但现实是 chain.filter 方法立刻返回了,直到 filter 过滤器全部 return 后才调用了模拟接口。

原因是:chain.filter 是个异步操作。

解决方案:利用 response 装饰者,增强原有response 的处理能力

@Component
@Slf4j
public class AynuFilter implements GlobalFilter ,Ordered{private static  final List<String> IP_WHITE_LIST = Arrays.asList("127.0.0.1");@AutowiredRedisTemplate redisTemplate;@AutowiredUserMapper userMapper;public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.用户发送请求到api网关log.info("进入网关过滤器");//2.配置请求日志ServerHttpRequest request = exchange.getRequest();log.info("请求唯一标识:{}",request.getId());log.info("请求路径:{}",request.getPath().value());log.info("请求方法:{}",request.getMethod());log.info("请求参数:{}",request.getQueryParams());log.info("请求来源地址:{}",request.getRemoteAddress());String hostString = request.getLocalAddress().getHostString();log.info("请求来源地址:{}",hostString);//3.黑白名单//获取响应对象ServerHttpResponse response = exchange.getResponse();if (!IP_WHITE_LIST.contains(hostString)){//设置响应状态码为403(禁止访问)response.setStatusCode(HttpStatus.FORBIDDEN);return response.setComplete();}//4.用户鉴权HttpHeaders headers = request.getHeaders();//调用者传过来的参数String accessKey = headers.getFirst("accessKey");String body = headers.getFirst("body");String timestamp = headers.getFirst("timestamp");String random = headers.getFirst("random");String sign = headers.getFirst("sign");//验证随机数,使用redis存储,以sign为键名//操作字符串数据对象ValueOperations valueOperations = redisTemplate.opsForValue();//getString randomDB = (String) valueOperations.get(sign);if (randomDB==null){//setex    TimeUnit是一个枚举类,里面列举了时间单位valueOperations.set(sign,random,120, TimeUnit.HOURS);}else {if (!randomDB.equals(random)){throw new RuntimeException("无权限");}}//调用mapper校验keyUser userDB = userMapper.getUserByAccessKey(accessKey);if (userDB==null){throw new RuntimeException("无权限");}//时间戳验证,时间戳不能和当前时间超过5分钟if (TimeUtils.checkTimesTamp(timestamp)){throw new RuntimeException("无权限");}//秘钥验证,使用传过来的数据生成sign,查询与用户的sign是否一致HashMap<String, String> hashMap = new HashMap<>();hashMap.put("accessKey",accessKey);hashMap.put("body",body);hashMap.put("random", random);hashMap.put("timestamp",timestamp);String newSign = SignUtils.getSign(hashMap, userDB.getSecretKey());if (!newSign.equals(sign)){throw new RuntimeException("无权限");}//5.请求的模拟接口是否存在//todoreturn  handleResponse(exchange,chain);}public Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain) {try {ServerHttpResponse originalResponse = exchange.getResponse();DataBufferFactory bufferFactory = originalResponse.bufferFactory();HttpStatus statusCode = originalResponse.getStatusCode();if (statusCode != HttpStatus.OK) {return chain.filter(exchange);//降级处理返回数据}ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {Flux<? extends DataBuffer> fluxBody = Flux.from(body);return super.writeWith(fluxBody.buffer().map(dataBuffers -> {//添加调用接口后的处理逻辑//8.调用成功,接口调用次数+1// 合并多个流集合,解决返回体分段传输DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer buff = dataBufferFactory.join(dataBuffers);byte[] content = new byte[buff.readableByteCount()];buff.read(content);DataBufferUtils.release(buff);//释放掉内存//构建日志StringBuilder stringBuilder = new StringBuilder(200);ArrayList<Object> arrayList = new ArrayList<>();arrayList.add(originalResponse.getStatusCode());String s = new String(content, StandardCharsets.UTF_8);stringBuilder.append(s);log.info("响应结果:{}", arrayList.toArray());return bufferFactory.wrap(content);}));} else {log.error("<-- {} 响应code异常", getStatusCode());}return super.writeWith(body);}};return chain.filter(exchange.mutate().response(decoratedResponse).build());} catch (Exception e) {log.error("gateway log exception.\n" + e);return chain.filter(exchange);}}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript解构赋值

一、数组解构 以上要么不好记忆&#xff0c;要么书写麻烦&#xff0c;此时可以使用解构赋值的方法让代码更简洁。 数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。 基本语法&#xff1a; 1、赋值运算符左侧的[]用于批量声明变量&#xff0c;右侧数组的单元值将…

MyBatis的各种查询功能

1、查询&#xff1a; 查询的标签select必须设置属性resultType或resultMap&#xff0c;用于设置实体类和数据库表的映射关系 resultType&#xff1a;自动映射&#xff0c;用于属性名和表中字段名一致的情况 resultMap&#xff1a;自定义映射&#xff0c;用于一对多或多对一或…

网络协议二

一、套接字Socket 基于 TCP UDP 协议的 Socket 编程&#xff0c;在讲 TCP 和 UDP 协议的时候&#xff0c;我们分客户端和服务端&#xff0c;在写程序的时候&#xff0c;我们也同样这样分。 在网络层&#xff0c;Socket 函数需要指定到底是 IPv4 还是 IPv6&#xff0c;分别对应设…

部署专属网页版ChatGPT-Next-Web

背景 工作学习中经常使用chat-gpt, 需求是多端使用gpt问答&#xff0c;因此搭建一个网页版本方便多个平台使用。最后选择了 ChatGPT-Next-Web 部署说明 一键部署自己的web页面&#xff0c;因为是使用免费的vercel托管的&#xff0c;vercel节点在全球都有&#xff0c;理论上突…

HSC Mailinspector loader.php 任意文件读取漏洞复现(CVE-2024-34470)

0x01 产品简介 HSC Mailinspector是一款远程电子邮件检查工具&#xff0c;支持POP3/IMAP4协议。它允许用户远程扫描最新邮件&#xff0c;并进行浏览、垃圾邮件排除、编辑、删除等操作&#xff0c;无需实际登录邮箱。 0x02 漏洞概述 由于HSC Mailinspector /public/loader.ph…

linux,lseek,append用法

打开写的.c文件 内容为 代码 <sys/stat.h> #include <fcntl.h> #include<stdio.h> #include<unistd.h> #include<string.h>//off_t lseek(int fd, off_t offset, int whence); //int open(const char *pathname, int flags); //int open(const …

【c++入门】函数重载,引用,内联函数,auto

函数重载 函数重载概念 什么是函数重载&#xff1f; 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类…

基于鲲鹏服务器搭建简单的开源论坛系统(LAMP)实践分享

LAMPLinux apache mysql( mariadb) PHP 结合利用华为云弹性负载均衡ELB弹性伸缩AS服务 优点&#xff1a; 将访问流量自动分发到多台云服务器&#xff0c;扩展应用系统对外的服务能力&#xff0c;实现更高水平的应用容错&#xff1b; 根据不同的业务、访问需求和预设策略&…

Java编程常见问题汇总一

系列文章目录 文章目录 系列文章目录前言一、字符串连接误用二、错误的使用StringBuffer三、测试字符串相等性四、数字转换成字符串五、利用不可变对象(Immutable) 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分…

mac电脑用谷歌浏览器对安卓手机H5页面进行inspect

1、mac上在谷歌浏览器上输入 chrome://inspect 并打开该页面。 2、连接安卓手机到Mac电脑&#xff1a;使用USB数据线将安卓手机连接到Mac电脑。 3、手机上打开要的h5页面 Webview下面选择要的页面&#xff0c;点击inspect&#xff0c;就能像谷歌浏览器页面打开下面的页面&#…

大模型时代的具身智能系列专题(七)

北大王鹤团队 王鹤&#xff0c;北京大学前沿计算研究中心助理教授&#xff0c;本科毕业于清华大学&#xff0c;博士毕业于斯坦福大学&#xff0c;师从美国三院院士Leonidas. J Guibas教授。他创立并领导了具身感知与交互实验室(EPIC Lab)&#xff0c;实验室立足三维视觉感知与…

矩阵连乘问题

#include<iostream> using namespace std; #define N 7 void MatrixChain(int p[N],int n,int m[N][N],int s[N][N]) {for(int i1;i<n;i)m[i][i]0;for(int r2;r<n;r)//有多少个相乘(规模){for(int i1;i<n-r1;i){int jir-1;m[i][j]m[i][i]m[i1][j]p[i]*p[i1]*p[j…

【AREngine BUG 解决方法】无法获取有效的相机图像尺寸

近期拿了一台 华为mate20 Pro的手机&#xff0c;在运行AR示例的过程中出现了黑屏。 问题排查 SDK版本&#xff1a;com.huawei.hms:arenginesdk:3.7.0.3 定位 经排查&#xff0c;发现(ARCamera对象的相机内参) getImageDimensions()返回的图像尺寸的width和height都为0。 这…

Vue——初识组件

文章目录 前言页面的构成何为组件编写组件组件嵌套注册 效果展示 前言 在官方文档中&#xff0c;对组件的知识点做了一个很全面的说明。本篇博客主要写一个自己的案例讲解。 vue 官方文档 组件基础 页面的构成 说到组件之前&#xff0c;先大致说明下vue中页面的构成要素。 在…

太速科技-基于XC7V690T的12路光纤PCIe接口卡

基于XC7V690T的12路光纤PCIe接口卡 一、板卡概述 基于XC7V690T的12路光纤PCI-E接口卡&#xff0c;用于实现多通道高速光纤数据接收和发送&#xff0c;板卡兼容PCIe 2.0和PCIe 3.0规范&#xff0c;利用PCI-E Switch PEX 8748实现FPGA芯片与计算机的通信&#xff0c;计算机与板…

小程序内的分包与数据共享

一:数据共享 小程序内的数据共享和vue当中不一样,vue当中的vue实例可以使得所有的组件都能this.store 但是小程序它只有page对象,和组件实例对象.对于vue而言,vue实例可以使得添加的组件都有. 但是page对象页面对象,不能使得页面内部有.只能使得这个页面内能访问.vue实例,会…

数据库 mysql 的彻底卸载

MySQL卸载步骤如下&#xff1a; &#xff08;1&#xff09;按 winr 快捷键&#xff0c;在弹出的窗口输入 services.msc&#xff0c;打开服务列表。 &#xff08;2&#xff09;在服务列表中&#xff0c; 找到 mysql 开头的所有服务&#xff0c; 右键停止&#xff0c;终止对应的…

LLM主流开源代表模型

LLM主流开源大模型介绍 1 LLM主流大模型类别 随着ChatGPT迅速火爆&#xff0c;引发了大模型的时代变革&#xff0c;国内外各大公司也快速跟进生成式AI市场&#xff0c;近百款大模型发布及应用。 目前&#xff0c;市面上已经开源了各种类型的大语言模型&#xff0c;本章节我们…

特征工程技巧—Bert

前段时间在参加比赛&#xff0c;发现有一些比赛上公开的代码&#xff0c;其中的数据预处理步骤值得我们参考。 平常我们见到的都是数据预处理&#xff0c;现在我们来讲一下特征工程跟数据预处理的区别。 数据预处理是指对原始数据进行清洗、转换、缩放等操作&#xff0c;以便为…

vue3状态管理,pinia的使用

​​​​​​​状态管理 我们知道组件与组件之间可以传递信息&#xff0c;那么我们就可以将一个信息作为组件的独立状态&#xff08;例如&#xff0c;单个组件的颜色&#xff09;或者共有状态&#xff08;例如&#xff0c;多个组件是否显示&#xff09;在组件之传递&#xff0c…