[Spring Cloud] (8)gateway完整性校验

简述

本文涉及代码已开源

本文网关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. 保障业务连续性:系统的完整性对于确保业务连续性和最小化潜在的业务中断至关重要。

作用:

  1. 数据验证:通过确保数据的完整性,网关可以验证数据在传输过程中未被更改,从而保护数据的真实性和可靠性。
  2. 安全审计:完整性检查可以作为安全审计的一部分,帮助检测和记录任何未经授权的尝试更改数据或系统的行为。
  3. 事故响应:在发生安全事件时,完整性保护措施可以帮助快速识别和隔离问题,从而减少损害并加速恢复过程。
  4. 增强防御:通过实施完整性保护,网关可以作为额外的安全层,增强整个系统的防御能力。

整体效果

前端如果不加特定参数将会被后端拦截。
image.png
image.png
前端经过处理之后,增加一个请求头参数C
即可通过后端的校验处理。
image.png

后端

增加完整性检验开关配置

  # 完整性校验
  reqIntegrity: true

image.png
GlobalConfig增加

    /**
     * 完整性校验
     */
    private boolean reqIntegrity;

image.png

完整性检验-请求拦截器

package com.fir.gateway.filter.request;

import com.alibaba.fastjson.JSONObject;
import com.fir.gateway.config.GlobalConfig;
import com.fir.gateway.config.exception.CustomException;
import com.fir.gateway.config.result.AjaxResult;
import com.fir.gateway.config.result.AjaxStatus;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;


/**
 * 完整性检验-请求拦截器
 *
 * @author fir
 */
@Slf4j
@Component
public class ReqIntegrityFilter implements GlobalFilter, Ordered {


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


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("完整性校验:start");
        boolean reqIntegrity = globalConfig.isReqIntegrity();

        if(reqIntegrity) {
            ServerHttpRequest request = exchange.getRequest();

            // 获取请求的相关信息
            HttpMethod method = request.getMethod();
            String path = request.getPath().toString();

            // 白名单路由判断
            List<String> whiteUrls = globalConfig.getWhiteUrls();
            if(whiteUrls.contains(path)){
                log.info("完整性校验:true,白名单");
                return chain.filter(exchange);
            }
            MultiValueMap<String, String> query = request.getQueryParams();
            String queryJson = JSONObject.toJSONString(query);
            // request.getQueryParams() 获取到的参数使得前后端完整性参数不一致,故暂时使用去除中括号[]的方式使得前后端统一
            queryJson = queryJson.replaceAll("\\[|\\]", "");
            queryJson = queryJson.replaceAll("\\\"", "");


            // 计算请求的完整性校验值
            String calculatedChecksum;
            if (method != null) {
                calculatedChecksum = calculateChecksum(method, path, queryJson);
            } else {
                log.error("完整性校验失败:请求类型获取失败,请求类型为空");
                throw new CustomException(AjaxStatus.INTEGRITY_VERIFY_FAILED);
            }

            // 获取请求中携带的校验值
            String providedChecksum = request.getHeaders().getFirst("c");

            // 比较校验值
            if (calculatedChecksum != null && calculatedChecksum.equals(providedChecksum)) {
                log.info("完整性校验:true");
                // 校验通过,继续处理请求
                return chain.filter(exchange);
            } else {
                log.info("完整性校验:false");
                // 校验失败,拒绝请求

                // 如果不合法,则返回错误响应
                ServerHttpResponse response = exchange.getResponse();
                // 自定义返回体描述
                AjaxResult error = AjaxResult.error(AjaxStatus.INTEGRITY_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);
    }

    @SneakyThrows
    private String calculateChecksum(HttpMethod method, String path, String query) {
        // TODO: 根据具体需求计算请求的完整性校验值,可以使用哈希算法或消息认证码

        // 示例:使用HMAC-SHA256计算校验值
        String data = method.toString() + path + query;
//        byte[] secretKeyBytes = SECRET_KEY.getBytes(StandardCharsets.UTF_8);
//        SecretKeySpec keySpec = new SecretKeySpec(secretKeyBytes, "HmacSHA256");
//        Mac mac = Mac.getInstance("HmacSHA256");
//        mac.init(keySpec);
//        byte[] checksumBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
//        // 将解密结果转为字符串
//        return Base64.getEncoder().encodeToString(checksumBytes);

        byte[] bytes = data.getBytes();

        //Base64 加密

        return Base64.getEncoder().encodeToString(bytes);
    }

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

前端

增加全局配置

// 完整性
const reqIntegrity = true;

image.png

请求拦截器添加完整性

gatewayRequest中增加

            // 完整性校验信息
            if (reqIntegrity) {
                request.headers.c = this.calculateChecksum(request.method, request.url, request.params)
            }

image.png

完整性签名生成

    //************************************完整性start
    /**
     * 完整性签名生成
     *
     * @param method 请求类型
     * @param path 请求地址
     * @param query 请求参数
     * @returns {*} 签名密钥
     */
    calculateChecksum(method, path, query) {
        let param
        if (query === null || query === undefined) {
            param = "{}"
        } else {
            param = JSON.stringify(query)
            param = param.replace(/\[|\]/g, '');
            param = param.replace(/"/g, '');
        }
        // 将请求的方法、路径和查询参数拼接成一个字符串
        let data = `${method.toUpperCase()}${path}${param}`;
        return this.gBase(data);
    },
    //************************************完整性-end

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柒杉丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值