《电商支付网关层轻量校验拦截案例及效果》
支付网关层轻量校验拦截案例
某电商支付网关日均处理请求超 1 亿次,通过在网关层前置拦截无效请求,将业务系统的无效请求占比从 30% 降至 5% 以下。以下是具体校验场景与实现:
一、签名校验:拦截伪造请求
场景
黑客通过抓包获取支付请求参数后,篡改金额(如将 100 元改为 1 元)并重新发送,企图恶意下单。
校验逻辑
-
商户调用支付接口前,需用双方约定的密钥对请求参数(如订单号、金额、时间戳)按 ASCII 排序后拼接,生成签名(如 MD5 或 SHA256)。
-
网关层接收请求时,提取参数与签名,用相同密钥和算法重新计算签名,对比一致则放行,否则拦截。
示例代码(基于 Spring Cloud Gateway)
@Component
public class SignatureVerifyFilter implements GlobalFilter {
@Override
public Mono\<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 提取请求参数
MultiValueMap\<String, String> params = exchange.getRequest().getQueryParams();
String requestSign = params.getFirst("sign");
String merchantId = params.getFirst("merchantId");
// 1. 校验签名是否存在
if (StringUtils.isEmpty(requestSign)) {
return returnError(exchange, "签名缺失");
}
// 2. 获取商户密钥(从配置中心获取)
String secretKey = merchantConfigService.getSecretKey(merchantId);
if (StringUtils.isEmpty(secretKey)) {
return returnError(exchange, "商户不存在");
}
// 3. 重新计算签名
String calculatedSign = SignatureUtils.calculateSign(params, secretKey);
if (!calculatedSign.equals(requestSign)) {
return returnError(exchange, "签名错误");
}
return chain.filter(exchange);
}
private Mono\<Void> returnError(ServerWebExchange exchange, String message) {
exchange.getResponse().setStatusCode(HttpStatus.BAD\_REQUEST);
return exchange.getResponse().writeWith(Mono.just(
exchange.getResponse().bufferFactory().wrap(message.getBytes())
));
}
}
效果
-
拦截率:日均拦截约 50 万次伪造请求,占总请求的 5%。
-
业务系统收益:无需处理伪造请求,CPU 使用率降低 15%。
二、参数合法性检查:拦截无效数据
场景
用户误输入金额(如负数、超过最大限额),或恶意传入超长字符串(如订单号长度超过 32 位),导致下游系统解析异常。
校验逻辑
-
网关层通过预定义的参数规则(如金额 > 0、订单号格式为字母 + 数字、长度 16-32 位)校验。
-
对必填参数(如订单号、支付方式)进行非空检查,缺失则直接拦截。
示例配置(基于 JSON Schema)
// 支付请求参数校验规则(存于配置中心)
{
"type": "object",
"properties": {
"orderNo": { "type": "string", "pattern": "^\[A-Za-z0-9]{16,32}\$" },
"amount": { "type": "number", "minimum": 0.01, "maximum": 100000 },
"payType": { "type": "string", "enum": \["alipay", "wechat", "unionpay"] }
},
"required": \["orderNo", "amount", "payType"]
}
效果
-
拦截率:日均拦截 100 万次参数错误请求(如金额为 0、支付方式不存在),占总请求的 10%。
-
下游影响:避免业务系统因参数错误抛出异常,减少 50% 的日志量。
三、防重放攻击:拦截重复请求
场景
黑客重复发送同一支付请求(如用户已支付 100 元,黑客重复提交该请求,企图扣两次款)。
校验逻辑
- 客户端请求时携带
nonce(随机字符串,如 UUID)和timestamp(时间戳,精确到秒)。 - 网关层检查:
-
时间戳与当前时间差是否超过 5 分钟(防止过期请求)。
-
Redis 中是否存在
nonce+merchantId的键(存在则为重复请求),不存在则存入 Redis 并设置 5 分钟过期。
示例代码
@Component
public class AntiReplayFilter implements GlobalFilter {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono\<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
MultiValueMap\<String, String> params = exchange.getRequest().getQueryParams();
String nonce = params.getFirst("nonce");
String timestamp = params.getFirst("timestamp");
String merchantId = params.getFirst("merchantId");
// 1. 校验时间戳有效性(5分钟内)
long currentTime = System.currentTimeMillis() / 1000;
if (Math.abs(currentTime - Long.parseLong(timestamp)) > 300) {
return returnError(exchange, "请求已过期");
}
// 2. 校验nonce是否重复
String redisKey = "pay:nonce:" + merchantId + ":" + nonce;
Boolean isExist = redisTemplate.opsForValue().setIfAbsent(redisKey, "1", 300, TimeUnit.SECONDS);
if (isExist == null || !isExist) {
return returnError(exchange, "重复请求");
}
return chain.filter(exchange);
}
}
效果
-
拦截案例:某大促期间拦截 20 万次重复支付请求,避免用户重复扣款投诉。
-
关键指标:重复请求占比从 8% 降至 1% 以下。
四、风控规则:拦截黑名单 IP
场景
某 IP 地址 1 小时内发送 1000 次支付请求(远超正常用户行为),疑似恶意刷单或 DDoS 攻击。
校验逻辑
-
网关层通过 Redis 记录每个 IP 的请求次数(滑动窗口计数,如 1 分钟内最多 60 次)。
-
结合风控系统的黑名单(如历史有欺诈记录的 IP),直接拦截。
示例代码
@Component
public class IpRateLimitFilter implements GlobalFilter {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono\<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String clientIp = getClientIp(exchange);
// 1. 检查是否在黑名单
Boolean isBlack = redisTemplate.hasKey("risk:blacklist:ip:" + clientIp);
if (Boolean.TRUE.equals(isBlack)) {
return returnError(exchange, "IP已被限制");
}
// 2. 滑动窗口计数(1分钟内最多60次请求)
String key = "rate:ip:" + clientIp + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
Long count = redisTemplate.opsForValue().increment(key);
if (count != null && count == 1) {
redisTemplate.expire(key, 60, TimeUnit.SECONDS);
}
if (count != null && count > 60) {
return returnError(exchange, "请求过于频繁");
}
return chain.filter(exchange);
}
}
效果
-
拦截率:日均拦截 30 万次异常 IP 请求,占总请求的 3%。
-
安全收益:成功拦截某团伙控制的 1000 个 IP 发起的刷单攻击。
五、校验顺序与性能优化
-
执行顺序:按轻量到复杂排序(参数合法性→时间戳→签名→防重放→风控),尽早拦截无效请求。
-
性能优化:
-
签名校验等 CPU 密集操作:用本地缓存(Caffeine)缓存商户密钥,减少 DB 查询。
-
计数类操作:用 Redis Pipeline 批量执行命令,降低网络开销。
-
整体耗时:所有校验合计耗时 < 5ms,不影响正常请求响应速度。
总结
网关层通过上述轻量校验,日均拦截约 200 万次无效请求,占总请求量的 20% 以上,使业务系统能聚焦于核心支付逻辑。关键价值在于:
-
安全性:拦截伪造、重复、恶意请求,保障资金安全。
-
性能:减少下游系统的无效计算与 IO 开销,提升整体吞吐量。
-
可维护性:集中化校验规则,无需各业务系统重复实现。