gateway接口参数加解密

本文介绍了在JavaGateway中如何使用AES加密和SHA1withRSA进行数据加密和解密,以及如何进行参数校验和返回数据的加密处理,涉及SignatureValidationGatewayFilterFactory和SignatureResGatewayFilterFactory的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇介绍了多种加解密的使用java加密使用
本篇主要介绍在gateway网关中使用对参数解密和返回数据进行加密的操作

原理

下面使用的是AES加密 SHA1withRSA加签

1-用户使用拿到的AES秘钥和RSA私钥。对数据进行加密和加签
2-进行验签和时间的检验
3-将解密的数据返回到具体的调用方(通过特定的filter具体业务方是无感知加解密的)
4-将业务方数据加密返回给调用方

参数校验

@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
@Component
public class SignatureValidationGatewayFilterFactory extends AbstractGatewayFilterFactory {

    private final EtrGatewayProperties etrGatewayProperties;

    @Autowired
    public SignatureValidationGatewayFilterFactory(EtrGatewayProperties etrGatewayProperties) {
        this.etrGatewayProperties = etrGatewayProperties;
    }

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            if (HttpMethod.GET.matches(exchange.getRequest().getMethodValue())
                    || HttpMethod.POST.matches(exchange.getRequest().getMethodValue())) {
                ModifyRequestBodyGatewayFilterFactory.Config modifyRequestConfig = new ModifyRequestBodyGatewayFilterFactory.Config()
                        .setContentType(ContentType.APPLICATION_JSON.getMimeType())
                        .setRewriteFunction(String.class, String.class, (exchange1, originalRequestBody) -> {
                            try {
                                JSONObject jsonObject = JSONUtil.parseObj(originalRequestBody);
                                String appId = jsonObject.getStr(SignatureConstants.SIGN_APPID);
                                String sign = jsonObject.getStr(SignatureConstants.SIGN_KEY);
                                String signType = jsonObject.getStr(SignatureConstants.SIGN_TYPE);
                                String timeStamp = jsonObject.getStr(SignatureConstants.SIGN_TIME_STAMP);
                                EtrGatewayProperties.SinatureConfig first = etrGatewayProperties.getSignatures().stream()
                                        .filter(appConfig -> appConfig.getAppId().equalsIgnoreCase(appId))
                                        .findFirst().orElse(null);
                                if (Objects.isNull(first)) {
                                    log.error("appId:{}不合法", appId);
                                    return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                                }
                                if (StrUtil.isBlank(sign) || StrUtil.isBlank(signType) || !StrUtil.isNumeric(timeStamp)) {
                                    log.error("参数不合法:sign:{},signType:{},timestamp:{}", sign, signType, timeStamp);
                                    return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                                }

                                // 验证时间戳
                                if (!validateTimestamp(timeStamp, first)) {
                                    log.warn("Invalid timestamp for appId: {}", appId);
                                    return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                                }

                                // 验证签名
                                if (!validateSignature(jsonObject, appId, signType, sign, first)) {
                                    log.warn("Signature verification failed for appId: {}", appId);
                                    return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                                }

                                String dataStr = decryptData(jsonObject.getStr(SignatureConstants.SIGN_DATA), first.getAesKey());

                                if (StringUtils.isBlank(dataStr)) {
                                    return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                                }

                                exchange1.getRequest().mutate().header(SignatureConstants.SIGN_APPID, appId);
                                return Mono.just(dataStr);
                            } catch (Exception e) {
                                return Mono.error(e);
                            }
                        });

                return new ModifyRequestBodyGatewayFilterFactory()
                        .apply(modifyRequestConfig)
                        .filter(exchange, chain)
                        .onErrorResume(e -> handleFilterError(exchange, e));
            }

            return chain.filter(exchange);
        };
    }

    private boolean validateTimestamp(String timeStamp, EtrGatewayProperties.SinatureConfig appConfig) {
        Integer timestampExpire = Optional.ofNullable(appConfig.getTimeStampExpire()).orElse(300);
        DateTime time = DateUtil.parse(timeStamp, DateUtil.newSimpleFormat("yyyyMMddHHmmss"));
        long between = DateUtil.between(time, new Date(), DateUnit.SECOND);
        return Math.abs(between) <= timestampExpire;
    }

    private boolean validateSignature(JSONObject jsonObject, String appId, String signType, String sign, EtrGatewayProperties.SinatureConfig appConfig) {
        String publicKey = appConfig.getPublicKey();
        SignStrategy signStrategy = new DefaultSignStrategy();
        try {
            return signStrategy.verifyPost(jsonObject.getStr(SignatureConstants.SIGN_DATA), publicKey,
                    CipherType.valueOf(signType.toUpperCase()), sign);
        } catch (Exception e) {
            log.error("Signature verification failed for appId: {}", appId, e);
            return false;
        }
    }

    private String decryptData(String data, String aesKey) {
        AES aes = SecureUtil.aes(aesKey.getBytes());
        return aes.decryptStr(data);
    }

    private Mono<Void> handleFilterError(ServerWebExchange exchange, Throwable e) {
        if (e instanceof BizException) {
            log.error("Filter error: {}", e.getMessage());
            return signatureError(exchange);
        }
        return Mono.error(e);
    }

    private Mono<Void> signatureError(ServerWebExchange exchange) {
        log.warn("Signature error: {}", exchange.getRequest().getURI().getPath());
        ApiResult<Object> result = ApiResult.fail(GatewayErrorCodeEnum.SIGNATURE_ERROR);
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        DataBuffer buffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes());
        return response.writeWith(Mono.just(buffer)).then(Mono.defer(response::setComplete));
    }
}

返回参数加密

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
@Component
public class SignatureResGatewayFilterFactory extends ModifyResponseBodyGatewayFilterFactory {

    private final EtrGatewayProperties etrGatewayProperties;

    public SignatureResGatewayFilterFactory(ServerCodecConfigurer codecConfigurer, Set<MessageBodyDecoder> bodyDecoders,
                                            Set<MessageBodyEncoder> bodyEncoders, EtrGatewayProperties etrGatewayProperties) {
        super(codecConfigurer.getReaders(), bodyDecoders, bodyEncoders);
        this.etrGatewayProperties = etrGatewayProperties;
    }

    @Override
    public GatewayFilter apply(Config config) {
        config.setRewriteFunction(String.class, String.class, (serverWebExchange, s) -> {
            JSONObject resJson = JSONUtil.parseObj(s);
            Integer code = resJson.getInt("code");
            if (200 == code) {
                String dataStr = resJson.getStr("data");
                if (StrUtil.isNotBlank(dataStr)) {
                    String appId = serverWebExchange.getRequest().getHeaders().getFirst(SignatureConstants.SIGN_APPID);
                    if (StrUtil.isBlank(appId)) {
                        return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
                    }
                    AES aes = SecureUtil.aes(getAesKey(appId).getBytes());
                    resJson.set("data", aes.encryptBase64(dataStr));
                    return Mono.just(resJson.toString());
                }
            }
            return Mono.just(s);

        });
        ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter(config);
        gatewayFilter.setFactory(this);
        return gatewayFilter;
    }

    private String getAesKey(String appId) {
        EtrGatewayProperties.SinatureConfig first = etrGatewayProperties.getSignatures().stream()
                .filter(appConfig -> appConfig.getAppId().equalsIgnoreCase(appId))
                .findFirst().orElse(null);
        if (Objects.isNull(first)) {
            throw BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR);
        }

        String aesKey = first.getAesKey();
        if (StrUtil.isBlank(aesKey)) {
            throw BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR);
        }
        return aesKey;
    }
}

### 实现 Spring Cloud Gateway 请求参数和响应参数的全局加解密 为了实现在 Spring Cloud Gateway 中对所有接口的请求参数和响应参数进行加密和解密,可以通过创建 `GlobalFilter` 来处理这些操作。下面展示了具体的实现方法。 #### 创建 AES 加密工具类 首先,需要引入 Hutool 工具库以便于使用其中提供的 AES 加密功能: ```xml <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.10</version> </dependency> ``` 接着编写一个简单的 AES 工具类来进行字符串级别的加密与解密工作: ```java import cn.hutool.crypto.symmetric.AES; public class AesUtil { private static final String KEY = "your-secret-key"; // 密钥长度需满足AES标准 /** * 使用AES算法加密给定的内容. */ public static String encrypt(String content) { try { byte[] keyBytes = KEY.getBytes("UTF-8"); AES aes = new AES(keyBytes); return aes.encryptBase64(content); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } /** * 解密由aes加密过的内容. */ public static String decrypt(String encryptedContent) { try { byte[] keyBytes = KEY.getBytes("UTF-8"); AES aes = new AES(keyBytes); return aes.decryptStr(encryptedContent, "UTF-8"); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } ``` #### 编写全局过滤器 接下来,在项目里定义一个新的 Java 类作为全局过滤器来执行实际的数据转换任务: ```java import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class EncryptDecryptFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 处理入站请求前先尝试解析解码body部分 // 这里的逻辑应该根据实际情况调整, // 比如判断是否为POST/PUT等携带Body的方法, // 且只针对某些路径或服务应用此规则。 // 同样地,在返回response之后也需要对其进行编码处理。 // 下面是一个简化版的例子: // 假设我们已经得到了原始request body string形式的数据叫做originalRequestBodyString String originalRequestBodyString = ""; // 获取原生请求体的方式取决于框架版本等因素 if (!"".equals(originalRequestBodyString)) { // 如果存在非空的请求体,则对其实施解密 String decryptedRequestData = AesUtil.decrypt(originalRequestBodyString); // 更新exchange对象中的请求信息... // 继续传递修改后的exchange实例至下一个filter链节点 return chain.filter(exchange.mutate().request(/*更新过的*/).build()); } else { // 若无任何待解密内容则直接放行 return chain.filter(exchange); } } @Override public int getOrder() { return 0; // 设置优先级数值越低代表越早被执行 } // Response encryption logic should be implemented similarly here or within another dedicated global filter. } ``` 请注意上述代码片段仅为示意用途;真实场景下可能还需要考虑更多细节问题,比如如何安全有效地读取HTTP消息主体、怎样适当地设置响应头指示客户端采用何种方式进行后续交互等等[^2]。 对于响应数据加密而言,可以在同一个全局过滤器内完成,也可以单独再建立另一个专门负责响应阶段工作的过滤器组件。无论哪种方式都遵循类似的模式——截获即将发出的 HTTP 响应流,从中提取出要保护的信息加以变换后再送出去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值