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

背景

往往项目中的开放接口可能被别有用心者对其进行抓包然后对请求参数进行篡改,或者重复请求占用系统资源为此我们行业内使用比较多的策略是接口签名校验。签名校验的实现可以用注解+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.hqwc.cn/news/702590.html

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

相关文章

go程序中同一个包下为什么会存在多个同名的函数或变量

目录 背景 一探究竟 增加构建约束 & 不同格式的构建约束 背景 首先&#xff0c;这种情况显然是不符合编译规则的&#xff0c;我们都知道在同一个包下出现两个同名的函数、变量、常量等会编译不通过&#xff0c;那么怎么还会有这种现象存在&#xff1f; 如下&#xff0…

4 特征构造

4 特征构造 学习目标 知道未来信息的概念,及处理未来信息的方法掌握从原始数据构造出新特征的方法掌握特征变换的方法掌握缺失值处理的方法1 数据准备 1.1 梳理数据的内在逻辑 关系种类 一对一:一个用户有一个注册手机号 一对多:一个用户多笔借款 多对多:一个用户可以…

如何购买RAKsmart的国外高防服务器?

随着互联网的快速发展&#xff0c;网络安全问题日益凸显&#xff0c;特别是对于拥有在线业务的企业或个人来说&#xff0c;选择一款高防服务器显得尤为关键。美国RAKsmart作为知名的服务器提供商&#xff0c;其高防服务器因其卓越的性能和安全性&#xff0c;受到了广大用户的青…

阻抗控制理解之逆动态控制律

具有六个自由度的二阶机械系统&#xff0c;其特征是给定的质量、阻尼和刚度&#xff0c;称为机械阻抗。 用于运动控制的加速度解决方法&#xff0c;它旨在通过逆动力学控制律在加速度水平上解耦和线性化非线性机器人动力学。在与环境存在交互作用的情况下&#xff0c;控制律 考…

【Redis】Redis键值存储

大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#xff0c;但是也想日更的人。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4aa;&#x1f4aa;&#x1f4aa…

STM32-08-串口

文章目录 STM32 串口1. 数据通信的基本概念2. 串口通信协议3. 串口4. 相关寄存器5. MSP回调机制6. HAL库中断回调机制7. USART/UART异步通信配置步骤8. IO引脚复用功能9. 代码实现 STM32 串口 1. 数据通信的基本概念 通信方式&#xff1a; 数据传输方向&#xff1a; 数据同…

六西格玛培训证书攻略2024:一站式解决方案助你快速上手

目前&#xff0c;企业对于员工的专业能力和综合素质要求越来越高。六西格玛作为一种先进的质量管理方法&#xff0c;已经成为众多企业提升运营效率、降低成本的重要手段。张驰咨询针对2024年六西格玛培训证书考取&#xff0c;为广大学员制定了实用的攻略&#xff0c;帮助学员们…

网络故障快速定位的秘诀 - 基于 AnaTraf 全流量回溯分析

网络故障是每个 IT 从业者都深有体会的头疼问题。当网络出现异常时,如何快速定位故障原因,恢复网络正常运行,是考验运维能力的关键所在。借助 AnaTraf 网络流量分析仪的全流量回溯分析功能,您可以轻松应对各种复杂的网络问题,实现快速故障定位。 1. 网络故障分析的痛点 网络故…

C++自定义脚本文件执行

FunctionCall.h&#xff1a; #include <sstream> #include <string> #include <vector> // 函数调用 class FunctionCall { public: FunctionCall(); ~FunctionCall(); std::string call(const st…

失误删除也能救回,推荐前10款手机数据恢复软件!

在手机的日常使用中&#xff0c;不可避免的会误删数据&#xff0c;这些数据可能包括照片、视频、联系人、短信等重要信息。不过好在市面上有很多优秀的手机数据恢复软件&#xff0c;可以帮助我们从各种情况下恢复丢失的数据。 本文将为您推荐十大手机数据恢复软件&#xff0c;…

Xxl-job实现定时任务视频转码

搭建XXL-JOB 调度中心 首先下载xxl-job GitHub&#xff1a;GitHub - xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度平台XXL-JOB&#xff09; 码云&#xff1a;xxl-job: 一个分布式任务调度平台&#xff0c;其核心设计目标是开发迅速…

三大国密浏览器简单介绍-行云管家

国密浏览器是指支持国密算法SM2/SM3&#xff0c;支持基于国密算法的SSL协议&#xff0c;支持采用国密SSL证书实现HTTPS加密的网站正常访问的浏览器。今天我们行云管家小编就给大家简单介绍一下三大国密浏览器&#xff0c;以便大家了解。 三大国密浏览器简单介绍-行云管家 1、密…