[Spring Cloud] (7)gateway防重放拦截器

文章目录

  • 简述
    • 本文涉及代码已开源
    • Fir Cloud 完整项目
    • 防重放
      • 防重放必要性:
      • 防重放机制作用:
  • 整体效果
    • 后端进行处理
  • 后端
    • 增加防重放开关配置
    • 签名密钥 工具类
    • 防重放拦截器
  • 前端
    • 被防重放拦截
    • 增加防重放开关配置
    • 请求头增加防重放签名处理
    • 防重放验证处理函数
    • base64加密生成签名
    • 请求头信息增加

简述

本文涉及代码已开源

本文网关gateway,微服务,vue已开源到gitee
杉极简 / gateway网关阶段学习
https://gitee.com/dong-puen/gateway-stages

Fir Cloud 完整项目

该内容完整项目如下
Fir Cloud v1.0.0
https://gitee.com/dong-puen/fir-cloud
https://github.com/firLucky/fir-cloud
在这里插入图片描述

防重放

防重放攻击的必要性主要来自于网络安全中的一个核心原则:确保数据的完整性、机密性和不可否认性。重放攻击是一种常见的安全威胁,它利用网络通信的漏洞来重新发送之前捕获的通信数据,以欺骗系统执行未授权的操作。

防重放必要性:

  1. 数据完整性:防止攻击者通过重放攻击修改或替换数据,确保数据在传输过程中未被篡改。
  2. 防止身份盗用:防止攻击者利用截获的认证信息冒充合法用户,从而访问受保护的资源。
  3. 保护交易:在金融交易和电子商务中,防止攻击者通过重放交易来欺诈。
  4. 遵守法规:某些行业法规要求实施防重放机制,以符合数据保护和网络安全的法律要求。
  5. 维护信任:通过保护系统免受重放攻击,维护用户和系统之间的信任关系。

防重放机制作用:

  1. 验证时间戳:通过在消息中包含时间戳,并确保消息在合理的时间窗口内被接收,可以防止旧消息被重放。
  2. 数字签名:使用数字签名来验证消息的发送者和内容,确保消息未被篡改,并且发送者的身份得到验证。
  3. 应用层检查:在应用层进行额外的逻辑检查,例如验证用户的状态或会话,以防止重放攻击。
  4. 日志和监控:记录和监控网络活动,以便在发生重放攻击时能够快速检测和响应。

通过实施防重放机制,可以显著提高系统的安全性,保护关键数据和操作免受未授权的访问和篡改。在设计系统时,应考虑潜在的重放攻击,并采取适当的措施来防范。

整体效果

前端如果没有进行防重放信息处理,则会被直接拦截。

后端进行处理

image.png

后端

增加防重放开关配置

  # 防重放
  replay: true

    /**
     * 防重放攻击
     */
    private boolean replay;

image.png
image.png

签名密钥 工具类

package com.fir.gateway.utils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 签名密钥 工具类
 * 
 * @author fir
 */
public class SignatureUtils {

    /**
     * 解密前端防重放校验的签名密钥(失败未验证)
     *
     * @param signature 签名密钥
     * @param secretKey 加密密钥
     * @return 返回解密结果
     */
    public static String decryptSignature(String signature, String secretKey) {
        try {
            // 使用HMAC-SHA256算法
            Mac sha256Hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256Hmac.init(secretKeySpec);

            // 对签名进行Base64解码
            byte[] signatureBytes = Base64.getDecoder().decode(signature);

            // 进行解密操作
            byte[] decryptedBytes = sha256Hmac.doFinal(signatureBytes);

            // 将解密结果转为字符串
            String decryptedSignature = new String(decryptedBytes, StandardCharsets.UTF_8);

            return decryptedSignature;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 解密前端防重放校验的签名密钥
     *
     * @param signature 签名密钥
     * @return 返回解密结果
     */
    public static String decryptSignatureBase64(String signature) {
        try {
            // 对签名进行Base64解码
            byte[] signatureBytes = Base64.getDecoder().decode(signature);

            // 将解密结果转为字符串

            return new String(signatureBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

防重放拦截器

package com.fir.gateway.filter.request;

import com.alibaba.fastjson.JSONObject;
import com.fir.gateway.config.GlobalConfig;
import com.fir.gateway.config.result.AjaxResult;
import com.fir.gateway.config.result.AjaxStatus;
import com.fir.gateway.dto.ReplayAttackInfo;
import com.fir.gateway.utils.SignatureUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * 防重放攻击-请求拦截器
 *
 * @author fir
 */
@Component
@Slf4j
public class ReplayAttackFilter implements GlobalFilter, Ordered {


    /**
     * 网关参数配置
     */
    @Resource
    private GlobalConfig globalConfig;


    /**
     * 5 * 60 * 1000 表示5分钟的间隔,用于防重放的间隔之中
     */
    private static final long TIMESTAMP_VALID_TIME = 5 * 60 * 1000;

    private final Set<String> usedNonceSet = Collections.synchronizedSet(new HashSet<>());


    /**
     * 每分钟执行一次
     */
    @Scheduled(cron = "0 * * * * *")
    public void clearUsedNonceSet() {
        usedNonceSet.clear();
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("防重放攻击验证:start");
        boolean replay = globalConfig.isReplay();
        if(replay) {
            // 白名单路由判断
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getPath().toString();
            List<String> whiteUrls = globalConfig.getWhiteUrls();
            if(whiteUrls.contains(path)){
                log.info("防重放攻击验证:true,白名单");
                return chain.filter(exchange);
            }

            // 从请求头中获取Nonce和Timestamp
            String nonce = exchange.getRequest().getHeaders().getFirst("n");
            String timestamp = exchange.getRequest().getHeaders().getFirst("t");
            String s = exchange.getRequest().getHeaders().getFirst("s");
            // 验证Nonce和Timestamp是否合法
            boolean validateKey = validateNonceAndTimestamp(nonce, timestamp, s);
            if (validateKey) {
                // 如果合法,则放行请求
                log.info("防重放攻击验证:true");
            } else {
                log.info("防重放攻击验证:false");
                // 如果不合法,则返回错误响应
                ServerHttpResponse response = exchange.getResponse();
                // 自定义返回体描述
                AjaxResult error = AjaxResult.error(AjaxStatus.ANTI_REPLAY_VERIFY_FAILED);
                String resData = JSONObject.toJSONString(error);

                byte[] responseBody = resData.getBytes(StandardCharsets.UTF_8);

                response.getHeaders().setContentLength(responseBody.length);
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

                return response.writeWith(Mono.just(response.bufferFactory().wrap(responseBody)));

            }
        }else {
            log.info("防重放攻击验证:true,验证已关闭");
        }

        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        // 设置过滤器的优先级
        return -200;
    }


    /**
     * 根据请求时间戳,与请求签名密钥,判断请求是否是合法的
     *
     * @param nonce 请求签名密钥
     * @param timestamp 请求时间戳
     * @return 是否合法
     */
    private boolean validateNonceAndTimestamp(String nonce, String timestamp, String s) {
        // 判断Nonce和Timestamp是否为空
        if (nonce == null || timestamp == null) {
            log.error("防重放攻击验证:非法请求,无请求时间戳");
            return false;
        }

        // 验证Nonce是否已经使用过
        if (usedNonceSet.contains(nonce)) {
            log.error("防重放攻击验证:请求签名已使用");
            return false;
        } else {
            // 将本次的请求签名记录,用于下次判断是否有相同的请求签名
            usedNonceSet.add(nonce);
        }

        // 判断事件戳与数据签名是否相同
        String str = SignatureUtils.decryptSignatureBase64(nonce);
        ReplayAttackInfo replayAttackInfo = JSONObject.parseObject(str, ReplayAttackInfo.class);
        String t = replayAttackInfo != null ? replayAttackInfo.getT() : null;
        if (StringUtils.isBlank(t) || !timestamp.equals(t)){
            log.error("防重放攻击验证:非法请求,请求时间非法");
            return false;
        }

        // 验证Timestamp是否在合理时间范围内
        long timeStampValue;
        try {
            timeStampValue = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            log.error("防重放攻击验证:非法请求,请求时间错误");
            return false;
        }

        long currentTime = System.currentTimeMillis();

        // 判断请求是是否是在n分钟之前请求的
        boolean a = timeStampValue >= currentTime - TIMESTAMP_VALID_TIME;
        // 判断请求是是否是在n分钟后前请求的
        boolean b = timeStampValue <= currentTime + TIMESTAMP_VALID_TIME;

        boolean c = a && b;
        if (!c){
            log.info("防重放攻击验证:请求过期");
        }
        return c;
    }
}

前端

被防重放拦截

image.png
增加nonce(签名),t(时间戳)。

增加防重放开关配置

// 防重放
const replay = true;

image.png

请求头增加防重放签名处理

gatewayRequest中增加

            // 防重放请求通过信息
            if (replay) {
                this.replayAttack(request);
            }

image.png

防重放验证处理函数

    //************************************防重放-start
    /**
     * 防重放验证
     *
     * @param config Axios请求的配置对象
     */
    replayAttack(config) {
        let params = config.params;
        let reqData = config.params;
        let t = new Date().getTime();
        if (t) {
            config.headers.t = t

            if (reqData == null) {
                params = {
                    't': t,
                }
            } else {
                // params = JSON.parse(reqData)
                params["t"] = t;
            }

            const data = JSON.stringify(params);
            config.headers.n = this.gBase(data);
        }
    },
    //************************************防重放-end

base64加密生成签名

    /**
     * base64加密
     * @param data data 待加密数据
     */
    gBase(data) {
        return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data));
    },

请求头信息增加

此时请求头中会增加两个信息,一个是时间戳,一个是签名。后端此时就会验证信息是否合法,合法则放行。
image.png

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

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

相关文章

HC-06 蓝牙串口从机 AT 命令详解

HC-06 蓝牙串口从机 AT 命令详解 要使用 AT 命令&#xff0c;首先要知道 HC-06 的波特率&#xff0c;然后要进入 AT 命令模式。 使用串口一定要知道三要素&#xff0c;一是波特率&#xff0c;二是串口号&#xff0c;三是数据格式, HC-06只支持一种数据格式: 数据位8 位&#…

MYSQL数据库-SQL语句

数据库相关概念 名称全称简称数据库存储数据的仓库&#xff0c;数据是有组织的进行存储DataBase(DB)数据库管理系统操纵和管理数据库的大型软件DataBase Management System(DBMS)SQL操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准Structured Quer…

第十四篇:数据库设计精粹:规范化与性能优化的艺术

数据库设计精粹&#xff1a;规范化与性能优化的艺术 1. 引言 1.1 数据库设计在现代应用中的核心地位 在数字化的浪潮中&#xff0c;数据库设计如同建筑师手中的蓝图&#xff0c;是构建信息大厦的基石。它不仅关乎数据的存储与检索&#xff0c;更是现代应用流畅运行的生命线。…

打印图形(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i, j;//循环打印&#xff1b;for (i 0; i < 5; i){//列&#xff1b;for (j 0; j &…

Python深度学习基于Tensorflow(9)注意力机制

文章目录 注意力机制是怎么工作的注意力机制的类型 构建Transformer模型Embedding层注意力机制的实现Encoder实现Decoder实现Transformer实现 注意力机制的主要思想是将注意力集中在信息的重要部分&#xff0c;对重要部分投入更多的资源&#xff0c;以获取更多所关注目标的细节…

关于Speech processing Universal PERformance Benchmark (SUPERB)基准测试及衍生版本

Speech processing Universal PERformance Benchmark &#xff08;SUPERB&#xff09;是由台湾大学、麻省理工大学&#xff0c;卡耐基梅隆大学和 Meta 公司联合提出的评测数据集&#xff0c;其中包含了13项语音理解任务&#xff0c;旨在全面评估模型在语音处理领域的表现。这些…

贝叶斯分类器详解

1 概率论知识 1.1 先验概率 先验概率是基于背景常识或者历史数据的统计得出的预判概率&#xff0c;一般只包含一个变量&#xff0c;例如P(A)&#xff0c;P(B)。 1.2 联合概率 联合概率指的是事件同时发生的概率&#xff0c;例如现在A,B两个事件同时发生的概率&#xff0c;记…

Hotcoin Research | 市场洞察:2024年5月6日-5月12日

加密货幣市场表现 加密货幣总市值为1.24万亿&#xff0c;BTC占比53.35%。 本周行情呈现先涨后跌的一种態势&#xff0c;5月6日-9日大盘持续下跌&#xff0c;周末为震荡行情。本周的比特幣现货ETF凈流入&#xff1a;1.1262亿美元&#xff0c;其中&#xff1a;美国ETF流入&…

Google与哈佛大学的科学家团队共同创造了一张人脑中一个极小部分的精细地图

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Linux重定向及缓冲区理解

重定向&#xff1a; 在上一期虚拟文件系统中讲到了每个进程在打开后&#xff0c;都会默认打开3个文件&#xff0c;如下&#xff1a; stdin 标准输入&#xff08;键盘&#xff09; 文件描述符&#xff1a;0 stdout 标准输出&#xff08;显示器&#xff09;文件描述符&a…

C++组合类

类的数据成员不但可以是基本类型&#xff0c;也可以是其它类的对象。 组合类就是指一个类包含其他类的对象作为该类的数据成员。 当组合类创建对象时&#xff0c;其中包含的各个数据成员对象应首先被创建。因此&#xff0c;在创建类的对象时&#xff0c;既要对本类的基本…

贪吃蛇(c实现)

目录 游戏说明&#xff1a; 第一个是又是封面&#xff0c;第二个为提示信息&#xff0c;第三个是游戏运行界面 游戏效果展示&#xff1a; 游戏代码展示&#xff1a; snack.c test.c snack.h 控制台程序的准备&#xff1a; 控制台程序名字修改&#xff1a; 参考&#xff1a…

DELL T630服务器iDRAC分辨率调整办法

对于Dell T630服务器的iDRAC分辨率调整&#xff0c;您需要登录到iDRAC的Web界面。以下是详细的步骤&#xff1a; 登录iDRAC&#xff1a;在浏览器中输入iDRAC的IP地址&#xff0c;然后使用用户名&#xff08;通常是“root”&#xff09;和密码登录。 导航到虚拟控制台&#xff…

(Java)心得:LeetCode——19.删除链表的倒数第 N 个节点

一、原题 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&…

IEEE 802.11标准

在IEEE 802.11标准中使用了扩频通信技术&#xff0c;主要作用是使得抗干扰性更强。 IEEE 802.11在MAC层采用了CSMA/CA协议。 IEEE 802.1x是一种基于端口认证协议。

报表-接口类型的数据源

1、配置 在数据中进行如下配置 配置格式&#xff0c;换行的方式 #API $.data[0].children http://192.168.1.1:9200/apis/getInfo 行1&#xff1a;固定写法&#xff0c;标识这是一个接口类型的数据集 行2&#xff1a;JSONPath格式字符串&#xff0c;对接口的数据进行取值。…

【半个月我拿下了软考证】软件设计师高频考点--系统化教学-关系模式

&#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件设计师考点暴击 ⭐&#x1f170;️进入狂砍分⭐ ⭐软件设计师高频考点文档&#xff0c; ⭐软件设计师高频考点专栏 ⭐软件设计师高频考点⭐ &#x1f3b6;&#xff08;A) 考点1,关系模式 考点&#xff1a; 三个模式相…

kettle经验篇:MongoDB-delete插件问题

目录 项目场景 问题分析 解决方案 MongoDB Delete插件使用总结 项目场景 项目使用的ODS层数据库是MongoDB&#xff1b;在数据中心从DB层向ODS层同步数据过程中&#xff0c;发现有张ODS表在同步过程中&#xff0c;数据突然发生锐减&#xff0c;甚至于该ODS表数据清0。 同步…

Zabbix6.0容器化部署(Docker-Composed)

Zabbix 为每个 Zabbix 组件提供 Docker image 作为可移植和自给自足的容器&#xff0c;以加快部署和更新过程。 Zabbix 组件在 Ubuntu、Alpine Linux 和 CentOS 基础 image 上提供:Zabbix 组件支持 MySQL 和 PostgreSQL 数据库、Apache2 和 Nginx Web 服务器。 1. Zabbix 组件…

17 SPI FLASH读写

SPI 协议简介 SPI 即 Serial Periphera linterface 的缩写&#xff0c;顾名思义就是串行外围设备接口&#xff0c;主要用于与FLASH、实时时钟、AD 转换器等外设模块的通信&#xff0c;它是一种高速的全双工同步的通信总线。 SPI 设备分为主设备和从设备&#xff0c;SPI 通信必…