网关过滤器实现接口签名检验

背景

往往项目中的开放接口可能被别有用心者对其进行抓包然后对请求参数进行篡改,或者重复请求占用系统资源为此我们行业内使用比较多的策略是接口签名校验。签名校验的实现可以用注解+aop的形式实现,也可以使用过滤器统一拦截校验实现,此篇文章博主将会介绍如何在过滤器中实现

思路

俗话说的好,要想干得好,脑子里得有明确的思路。博主以往的工作经历已经养成一个良好的习惯,任何需求编码前习惯先画出思维流程图
在这里插入图片描述
大致实现思路:

  1. 客户端 (参数+时间戳+随机串+密钥 )进行md5加密,将加密后的sign放在请求头里带到服务端
  2. 服务端在过滤器中获取请求头和请求体的数据
  3. 判断此次请求的时间戳和系统当前时间的偏移量是否大于1分钟(偏移时间量可以自行定义),如果大于可以判定为非法请求
  4. sign作为key查询缓存中是否存在,存在可以判定为重放请求(**此步骤也可忽略,接入限流处理)**
  5. 服务端按照客户端同样的规则生成sign签名,(参数+时间戳+随机串+密钥 )进行md5加密,判断请求的sign签名和生成的sign是否一致,不一致可以判定为参数已经被篡改
  6. 当上面校验都通过后,可以把sign签名作为key存入缓存,方便下次判断请求是否重放**此步骤也可忽略,接入限流处理)**
  7. 放行请求

代码实现

package cn.jian.yuan.gateway;import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBufAllocator;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
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.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.print.DocFlavor;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {private RedisTemplate<String, Object> redisTemplate;private StringRedisTemplate stringRedisTemplate;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();String sign = request.getHeaders().getFirst("sign");String timestamp = request.getHeaders().getFirst("timestamp");String nonce = request.getHeaders().getFirst("nonce");//直接放行的path集合List<String> releaserPathList = new ArrayList<>();releaserPathList.add("/*.html");releaserPathList.add("/**/*.js");String path = request.getURI().getPath();//直接放行的urlif (releaserPathList.contains(path)) {return chain.filter(exchange);}//判断必填参数if (StringUtils.isBlank(sign) || StringUtils.isBlank(timestamp) || StringUtils.isBlank(nonce)) {//自定义返回错误信息return returnResult(response, HttpStatus.BAD_REQUEST, "必填参数为空");}//判断请求发起时间和系统时间的偏移量if (Math.abs(System.currentTimeMillis()) - Long.parseLong(timestamp) > 60000L) {return returnResult(response, HttpStatus.REQUEST_TIMEOUT, "请求超时");}if (request.getMethodValue().equalsIgnoreCase(HttpPost.METHOD_NAME)) {/*--- 判断是否重放只在post请求里判断重放是因为博主认为get请求大部分都是查询,可以接入限流框架来处理并发。或者过滤器中直接不判断重放,全部由限流框架来处理*/if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(sign))) {return returnResult(response, HttpStatus.BAD_REQUEST, "请求重放");}String jsonBody = resolverBodyJsonRequest(request);if (!checkSign(timestamp, nonce, sign, jsonBody)) {return returnResult(response, HttpStatus.BAD_REQUEST, "非法请求");}//下面的将请求体再次封装会写到request里传到下一级,否则由于请求体已被消费,后续服务将取不到值ServerHttpRequest newRequest = request.mutate().uri(request.getURI()).build();DataBuffer dataBuffer = stringBuffer(jsonBody);Flux<DataBuffer> dataBufferFlux = Flux.just(dataBuffer);request = new ServerHttpRequestDecorator(newRequest) {@Overridepublic Flux<DataBuffer> getBody() {return dataBufferFlux;}};//有效期和上面请求时间偏移量保持一致redisTemplate.opsForValue().set(sign, "1", 60000L, TimeUnit.MILLISECONDS);} else if (request.getMethodValue().equalsIgnoreCase(HttpGet.METHOD_NAME)) {StringBuilder builder = new StringBuilder();MultiValueMap<String, String> queryParams = request.getQueryParams();for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {String key = entry.getKey();List<String> value = entry.getValue();builder.append("{\"").append(key).append("\":\"").append(value.get(0)).append("\"}");}if(!checkSign(timestamp, nonce, sign, builder.toString())){return returnResult(response, HttpStatus.BAD_REQUEST, "非法请求");}}return chain.filter(exchange.mutate().request(request).build());}private DataBuffer stringBuffer(String jsonBody) {byte[] bytes = jsonBody.getBytes(StandardCharsets.UTF_8);NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);NettyDataBuffer nettyDataBuffer = nettyDataBufferFactory.allocateBuffer(bytes.length);return nettyDataBuffer.write(bytes);}//参数拼接后进行md5加密对比private boolean checkSign(String timestamp, String nonce, String sign, String jsonBody) {//注意参数拼接顺序和数据类型要和前端保持一致,否则加密出来的结果不一样TreeMap map = JSON.parseObject(jsonBody, TreeMap.class);map.put("timestamp", timestamp);map.put("nonce", nonce);map.put("secret", "自定义密钥key");//不能泄漏,自己在代码中定义好即可String jsonString = JSON.toJSONString(map);String localSign = DigestUtils.md5DigestAsHex(jsonString.getBytes(StandardCharsets.UTF_8));return localSign.equals(sign);}//gateway 网关过滤器获取请求体json数据的方法private String resolverBodyJsonRequest(ServerHttpRequest request) {Flux<DataBuffer> body = request.getBody();AtomicReference<Object> reference = new AtomicReference<>();body.subscribe(dataBuffer -> {CharBuffer decode = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());DataBufferUtils.release(dataBuffer);reference.set(decode.toString());});return reference.get().toString();}private Mono<Void> returnResult(ServerHttpResponse response, HttpStatus httpStatus, String msg) {DataBuffer dataBuffer = response.bufferFactory().wrap(msg.trim().getBytes());response.getHeaders().add("content-type", "application/json;charset=UTF-8");response.setStatusCode(httpStatus);return response.writeWith(Mono.just(dataBuffer));}@Overridepublic int getOrder() {return -100;}
}

以上就完成了在Gateway 网关过滤器中完成了对接口的签名检验功能

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

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

相关文章

图形化编程桌面 跨部门协作的新工具

中午&#xff0c;阳光洒满大地&#xff0c;微风轻拂&#xff0c;给人带来温暖和活力。卧龙和凤雏在享用过午餐后&#xff0c;决定到公司附近的湖边散步&#xff0c;享受这难得的宁静时光。 卧龙望着湖面泛起的波光&#xff0c;顺手折下一根嫩绿的柳枝&#xff0c;在手中不停地摆…

运维别卷系列 - 云原生监控平台 之 04.prometheus 查询语句 promql 实践

文章目录 [toc]PromQL 简介什么是时间序列 PromQL 数据类型即时向量 Instant vector范围向量 Range vectorTime DurationsOffset modifier modifier 浮点值 Scalar字符串 String PromQL FUNCTIONSfloor()irate()rate()round()sort()sort_desc() PromQL 运算符算术运算符比较运算…

TortoiseGit的安装

TortoiseSvn和TortoiseGit都是针对代码进行版本管理的工具&#xff0c;又俗称小乌龟&#xff0c;简洁而可视化的操作界面&#xff0c;免去繁琐的命令行输入。只需要记住常用的几个操作步骤就能快速上手。 TortoiseGit安装 1、TortoiseGit作为git的版本管理工具 &#xff0c;但…

无感自动透明加密系统

无感透明加密系统是一种高级的数据安全技术&#xff0c;它在不影响用户正常操作的前提下&#xff0c;对指定的文件或数据进行自动加密和解密。这种系统设计的目的是为了提高信息安全级别&#xff0c;确保敏感信息在存储和传输过程中的保密性&#xff0c;同时又不会给合法用户的…

5.9网络协议

由网卡发送数据通过网线进行发送&#xff0c;当网卡接收到信号以后将数据传给内核数据区&#xff0c;然后由操作系统交给相应的进程。 将数据进行发送的时候需要借助于网线实现&#xff0c;这个时候会出现当传输的数据比较远的时候就借助于中继器将信号进行再生扩大&#xff0…

FedDML:Federated Mutual Learning

这篇把DML运用到FL上 论文地址:arvix code: 作者git 贡献 我们针对三种异质性(DOM)提出了一种新颖的联邦学习范式,称为联邦相互学习(FML)。 首先,FML 处理数据和目标通过使每个客户能够训练个性化模型来实现异质性。 从OH的意义上来说,DH对服务器有害,但对客户端有…

STL—string类(1)

一、string类 1、为什么要学习string&#xff1f; C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP&#xff08;面向对象…

爱校对新功能上线:领导人讲话和职务排序校对

我们很高兴地宣布&#xff0c;爱校对网站正式推出两项新功能&#xff1a;领导人讲话校对和领导人职务排序校对。这些新功能旨在帮助用户更准确地引用和整理领导人讲话内容&#xff0c;以及正确排列领导人的职务顺序。 领导人讲话校对 在撰写报告或文章时&#xff0c;引用领导…

气膜建筑使用寿命由什么决定—轻空间

气膜建筑作为一种新型建筑结构&#xff0c;以其独特的优点在全球范围内逐渐普及。其使用寿命是投资者和用户关注的关键问题。气膜建筑的使用寿命主要由以下几个方面决定&#xff1a; 1. 膜材 膜材是气膜建筑的核心组成部分&#xff0c;其质量直接影响到建筑的使用寿命。以下是影…

智能网红主播直播手机:助您轻松卖货、卖团购卷、拓客利器!

在当下快速发展的电商行业中&#xff0c;直播销售已经成为无可忽视的一大趋势。智能网红主播直播手机的出现&#xff0c;让人们无需拥有专业设备和经验&#xff0c;便可轻松参与直播销售&#xff0c;享受销售乐趣。本文将介绍智能网红主播直播手机的操作简单、易上手以及其在卖…

JAVA二手车交易二手车市场系统源码支持微信小程序+微信公众号+H5+APP

二手车交易二手车市场系统&#xff1a;重塑购车新体验 随着汽车消费市场的日益成熟&#xff0c;二手车交易逐渐成为消费者购车的新选择。为了提供更加便捷、透明、安全的二手车交易环境&#xff0c;我们推出了“二手车交易二手车市场系统”&#xff0c;旨在为买卖双方搭建一个…

新书速览|Django 5 Web应用开发实战

构建未来&#xff0c;用Django 5打造全新Web应用 本书内容 《Django 5 Web应用开发实战》集Django架站基础、项目实践、开发经验于一体&#xff0c;是一本从零基础到精通Django Web企业级开发技术的实战指南。《Django 5 Web应用开发实战》内容以Python 3.x和Django 5版本为基础…

JavaScript循环结构

JS循环结构 1 while结构2 for循环3 foreach循环 1 while结构 几乎和JAVA一致 代码 /* 打印99 乘法表 */var i 1;while(i < 9){var j 1;while(j < i){document.write(j"*"i""i*j" ");j;}document.write("<hr/>");i…

深度学习设计模式之简单工厂模式

文章目录 前言一、简单工厂设计模式的作用&#xff1f;二、详细分析1.核心组成2.实现步骤3.示例代码4.优缺点优点缺点 5.使用场景 总结 前言 本文主要学习简单工厂设计模式&#xff0c;这个设计模式主要是将创建复杂对象的操作单独放到一个类中&#xff0c;这个类就是工厂类&a…

物联网D3——按键控制LED、光敏传感蜂鸣器

按键控制LED 按键抖动&#xff0c;电平发生变化&#xff0c;可用延时函数抵消按键抖动对系统的影响 传感器电路图 按键电路图 c语言对应类型 “_t”后缀表示使用typedef重命名的数据类型 枚举类型 #include<iostream> using namespace std; //定义枚举类型 typedef enu…

基于springboot实现的在线动漫信息平台

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

vue2 结合iview和百度地图API实现电子围栏

vue2 结合iview和百度地图API实现电子围栏 实现在地图上绘制电子围栏并自定义电子围栏样式&#xff0c;还可以标记中心点 1.百度地图API相关JS引用 <script src"//api.map.baidu.com/api?typewebgl&v1.0&ak百度地图官网申请的ak"></script>//…

【JVM】调优工具

这里简单介绍一下各种调优用到的工具 一&#xff0c;环境准备 首先我们需要准备好Java环境&#xff0c;和win上的jdk环境&#xff08;图形化界面如jconsole只有jdk中有&#xff09;。 有这样一个类Prolem&#xff0c;每个线程都会带来100个垃圾对象&#xff0c;线程new完100…

翻译《The Old New Thing》- What does the CS_OWNDC class style do?

What does the CS_OWNDC class style do? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20060601-06/?p31003 Raymond Chen 2006年06月01日 简要 本文讨论了CS_OWNDC窗口类样式的影响&#xff0c;它让窗口管理器为窗口创建一个永久的设…

品牌银饰售卖|基于SSM+vue的品牌银饰售卖平台的设计与实现(源码+数据库+文档)

品牌银饰售卖平台 目录 基于SSM&#xff0b;vue的品牌银饰售卖平台的设计与实现 一、前言 二、系统设计 三、系统功能设计 1前台功能模块 2后台功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题…