当前位置: 首页 > news >正文

通过SpringCloudGateway中的GlobalFilter实现鉴权过滤

1.pom.xml中加入gateway jar包

    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>

2.创建权限过滤器 SecurityFilter

/*** 鉴权过滤***/
@Slf4j
@Component
public class SecurityFilter implements GlobalFilter, Ordered {@Resourceprivate MerchantAppApi merchantAppApi;/*** 签名算法*/private final static String SIGN_RULE = "HmacSHA256";/*** 通过sdkKey做鉴权过滤***/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取头信息HttpHeaders headers = exchange.getRequest().getHeaders();// 头信息 客户端签名String sign = headers.getFirst("sign");// 头信息 客户端timeStampString timeStamp = headers.getFirst("timeStamp");// 头信息 账户keyString sdkKey = headers.getFirst("sdkKey");//获取body strString bodyStr = RequestBodyUtil.resolveBodyFromRequest(exchange.getRequest());// 去空格bodyStr = StringUtils.replace(bodyStr, " ", "");String authDesc = "鉴权异常..";try {// rpc 账户信息GetMerchantAppResp merchantApp = merchantAppApi.getMerchantApp(new MerchantAppReq().setSdkKey(sdkKey)).getData();if (merchantApp == null) {authDesc = "账户不存在..";throw new IllegalAccessException("账户不存在..");}if (!merchantApp.getStatus()) {authDesc = "账户被禁用..";throw new IllegalAccessException("账户被禁用..");}// 验证签名String signStr = SecurityFilter.genSign(timeStamp, sdkKey, merchantApp.getSdkSecret(), bodyStr);if (!StringUtils.equals(sign, signStr)) {authDesc = "签名验证失败..";throw new IllegalAccessException("签名验证失败..");}} catch (Exception e) {log.error(String.format("=== %s ===, timeStampStr=%s, sdkKey=%s, requestBody=%s",authDesc, timeStamp, sdkKey, bodyStr), e);ServerHttpResponse response = exchange.getResponse();ApiResp apiResp = ApiResp.authFail(authDesc);byte[] bits = JacksonUtil.toJsonString(apiResp).getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(bits);// 200response.setStatusCode(HttpStatus.OK);//指定编码,否则在浏览器中会中文乱码response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}return chain.filter(exchange);}/*** 根据请求时间戳 以及请求body 生产签名** @param timeStamp 时间戳* @param bodyStr   实体类* @return 签名*/private static String genSign(String timeStamp, String sdkKey, String sdkSecret, String bodyStr) {String sign = "";try {String signResource = timeStamp + sdkKey + bodyStr;Mac mac = Mac.getInstance(SIGN_RULE);mac.init(new SecretKeySpec(sdkSecret.getBytes(StandardCharsets.UTF_8), SIGN_RULE));byte[] signatureBytes = mac.doFinal(signResource.getBytes(StandardCharsets.UTF_8));String hexStr = Hex.encodeHexString(signatureBytes);sign = Base64.encodeBase64String(hexStr.getBytes());} catch (Exception e) {log.error("=== 生成签名失败 ===, timeStampStr={}, sdkKey={}, sdkSecret={}, requestBody={}", timeStamp, sdkKey, sdkSecret, bodyStr);}return sign;}/*** 过滤顺序** @return 排序*/@Overridepublic int getOrder() {return 5;}}

3.封装工具类 RequestBodyUtil

/*** 操作request body的工具**/
public class RequestBodyUtil {private final  static  Pattern P = Pattern.compile("\\s*|\t|\r|\n");/*** 读取body内容** @param serverHttpRequest 请求对象* @return body*/public static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {//获取请求体Flux<DataBuffer> body = serverHttpRequest.getBody();StringBuilder sb = new StringBuilder();body.subscribe(buffer -> {byte[] bytes = new byte[buffer.readableByteCount()];buffer.read(bytes);String bodyString = new String(bytes, StandardCharsets.UTF_8);sb.append(bodyString);});return formatStr(sb.toString());}/*** 去掉空格,换行和制表符** @param str 待优化字符串* @return 格式化后的str*/private static String formatStr(String str) {if (str != null && str.length() > 0) {Matcher m = P.matcher(str);return m.replaceAll("");}return str;}}

相关文章:

  • 代码随想录第23天|回溯part3 组合与分割
  • 微服务学习Day8-Sentinel
  • Flink搭建
  • 【LeetCode】二叉树oj专题
  • elementplu父级页面怎么使用封装子组件原组件的方法
  • 【距离四六级只剩一个星期!】刘晓艳四级保命班课程笔记(2)(可分享治资料~)
  • 前端 html格式转md格式插件使用介绍
  • 解决JSON.stringify 方法在序列化 BigInt 类型时的错误
  • ardupilot开发 --- 机载计算机-软件方案 篇
  • 基于单片机的超声波倒车雷达设计
  • 汇舟问卷:国外问卷调查怎么样?
  • mysql like 查询优化
  • 遥感卫星影像处理流程
  • 3403(3519Dv500)算子精度比对工具标杆数据生成环境搭建指导(Caffe)
  • 【Python Cookbook】S01E15 将名称映射到序列的元素中
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • CSS3 变换
  • iOS | NSProxy
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • JDK9: 集成 Jshell 和 Maven 项目.
  • vue 配置sass、scss全局变量
  • Vue--数据传输
  • Web设计流程优化:网页效果图设计新思路
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 机器学习 vs. 深度学习
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 前端学习笔记之观察者模式
  • 前言-如何学习区块链
  • 网页视频流m3u8/ts视频下载
  • 如何在招聘中考核.NET架构师
  • ​学习一下,什么是预包装食品?​
  • # Redis 入门到精通(七)-- redis 删除策略
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • $L^p$ 调和函数恒为零
  • (35)远程识别(又称无人机识别)(二)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (NSDate) 时间 (time )比较
  • (补)B+树一些思想
  • (定时器/计数器)中断系统(详解与使用)
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .net core 控制台应用程序读取配置文件app.config
  • .NET I/O 学习笔记:对文件和目录进行解压缩操作
  • .NET Micro Framework初体验(二)
  • .Net Web窗口页属性
  • .net 后台导出excel ,word
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET8使用VS2022打包Docker镜像
  • @AliasFor 使用
  • @RequestBody的使用