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

API接口签名验证实战

API接口签名验证实战

一、接口签名概述

API签名验证是保护接口安全的重要手段,防止请求被篡改或伪造。

1.1 签名机制原理

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 客户端 │ │ 网络 │ │ 服务端 │ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ 1. 构造请求参数 │────▶│ 传输请求 │────▶│ 1. 获取请求参数 │ │ 2. 生成签名 │ │ │ │ 2. 重新生成签名 │ │ 3. 发送请求 │ │ │ │ 3. 验证签名 │ └─────────────────┘ └─────────────────┘ │ 4. 处理请求 │ └─────────────────┘

1.2 签名要素

要素说明示例
AppKey应用标识app001
AppSecret应用密钥xxxxxx
Timestamp时间戳1704067200
Nonce随机数abc123
Signature签名结果md5(xxx)

二、签名算法实现

2.1 签名生成流程

public class SignatureUtil { private static final String SIGN_METHOD = "MD5"; private static final String CHARSET = "UTF-8"; public static String generateSignature(Map<String, String> params, String appSecret) { // 1. 去除sign参数 params.remove("sign"); // 2. 按字典序排序 List<String> keys = new ArrayList<>(params.keySet()); Collections.sort(keys); // 3. 拼接参数 StringBuilder sb = new StringBuilder(); for (String key : keys) { String value = params.get(key); if (value != null && !value.isEmpty()) { sb.append(key).append("=").append(value).append("&"); } } // 4. 拼接密钥 sb.append("key=").append(appSecret); // 5. 计算签名 return md5(sb.toString()); } private static String md5(String input) { try { MessageDigest md = MessageDigest.getInstance(SIGN_METHOD); byte[] digest = md.digest(input.getBytes(CHARSET)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString().toUpperCase(); } catch (Exception e) { throw new RuntimeException("MD5 calculation failed", e); } } }

2.2 HMAC-SHA256签名

public class HmacSignatureUtil { private static final String ALGORITHM = "HmacSHA256"; public static String generateHmacSignature(Map<String, String> params, String appSecret) { params.remove("sign"); List<String> keys = new ArrayList<>(params.keySet()); Collections.sort(keys); StringBuilder sb = new StringBuilder(); for (String key : keys) { String value = params.get(key); if (value != null && !value.isEmpty()) { sb.append(key).append("=").append(value).append("&"); } } sb.append("key=").append(appSecret); try { Mac mac = Mac.getInstance(ALGORITHM); SecretKeySpec keySpec = new SecretKeySpec(appSecret.getBytes(), ALGORITHM); mac.init(keySpec); byte[] digest = mac.doFinal(sb.toString().getBytes()); return Base64.getEncoder().encodeToString(digest); } catch (Exception e) { throw new RuntimeException("HMAC calculation failed", e); } } }

三、签名验证实现

3.1 验证过滤器

public class SignatureFilter implements Filter { private static final long TIMESTAMP_TOLERANCE = 5 * 60 * 1000; // 5分钟 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; try { // 1. 获取参数 Map<String, String> params = getRequestParams(httpRequest); // 2. 验证时间戳 String timestamp = params.get("timestamp"); validateTimestamp(timestamp); // 3. 验证随机数 String nonce = params.get("nonce"); validateNonce(nonce); // 4. 获取AppKey String appKey = params.get("appKey"); String appSecret = getAppSecret(appKey); // 5. 验证签名 String sign = params.get("sign"); String expectedSign = SignatureUtil.generateSignature(params, appSecret); if (!sign.equalsIgnoreCase(expectedSign)) { httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.getWriter().write("Invalid signature"); return; } chain.doFilter(request, response); } catch (Exception e) { httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } } private void validateTimestamp(String timestamp) { long ts = Long.parseLong(timestamp); long now = System.currentTimeMillis() / 1000; if (Math.abs(now - ts) > TIMESTAMP_TOLERANCE / 1000) { throw new RuntimeException("Invalid timestamp"); } } private void validateNonce(String nonce) { if (nonce == null || nonce.length() < 8) { throw new RuntimeException("Invalid nonce"); } } }

3.2 Nonce去重

public class NonceManager { private final StringRedisTemplate redisTemplate; private static final String PREFIX = "nonce:"; private static final long EXPIRE_SECONDS = 5 * 60; // 5分钟 public void validateNonce(String nonce) { String key = PREFIX + nonce; Boolean exists = redisTemplate.hasKey(key); if (Boolean.TRUE.equals(exists)) { throw new RuntimeException("Duplicate nonce"); } redisTemplate.opsForValue().set(key, "true", EXPIRE_SECONDS, TimeUnit.SECONDS); } }

四、请求示例

4.1 客户端请求

public class ApiClient { private String appKey = "app001"; private String appSecret = "secret123"; public String sendRequest(String url, Map<String, Object> data) { Map<String, String> params = new HashMap<>(); params.put("appKey", appKey); params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); params.put("nonce", UUID.randomUUID().toString().replace("-", "").substring(0, 16)); for (Map.Entry<String, Object> entry : data.entrySet()) { params.put(entry.getKey(), String.valueOf(entry.getValue())); } String sign = SignatureUtil.generateSignature(params, appSecret); params.put("sign", sign); // 发送请求... return doPost(url, params); } }

4.2 curl示例

curl -X POST http://api.example.com/endpoint \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "appKey=app001" \ -d "timestamp=1704067200" \ -d "nonce=abc123def456" \ -d "data=test" \ -d "sign=A1B2C3D4E5F6"

五、安全加固

5.1 使用HTTPS

server: ssl: enabled: true key-store: classpath:keystore.p12 key-store-password: password key-store-type: PKCS12 key-alias: tomcat

5.2 IP白名单

public class IpWhitelistFilter implements Filter { private Set<String> whitelist = Set.of( "192.168.1.1", "10.0.0.0/8" ); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String clientIp = getClientIp((HttpServletRequest) request); if (!isIpWhitelisted(clientIp)) { ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN); return; } chain.doFilter(request, response); } }

5.3 频率限制

public class RateLimitFilter implements Filter { private final StringRedisTemplate redisTemplate; private static final int MAX_REQUESTS = 100; private static final int TIME_WINDOW_SECONDS = 60; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { String clientIp = getClientIp((HttpServletRequest) request); String key = "rate_limit:" + clientIp; Long count = redisTemplate.opsForValue().increment(key); if (count == 1) { redisTemplate.expire(key, TIME_WINDOW_SECONDS, TimeUnit.SECONDS); } if (count > MAX_REQUESTS) { ((HttpServletResponse) response).setStatus(429); return; } chain.doFilter(request, response); } }

六、签名算法对比

算法安全性性能适用场景
MD5内部系统
SHA-1一般场景
SHA-256重要场景
HMAC-SHA256对外API

七、最佳实践

7.1 签名规范

  1. 参数排序:按字典序升序排列
  2. 空值处理:忽略空参数
  3. 编码格式:统一使用UTF-8
  4. 签名格式:统一大写或小写

7.2 安全建议

  1. 密钥管理:使用密钥管理服务存储密钥
  2. 定期轮换:定期更换AppSecret
  3. 日志审计:记录签名验证失败日志
  4. 异常监控:监控异常签名请求

7.3 常见问题

问题原因解决方案
签名不一致参数顺序不同统一排序规则
时间戳过期客户端时间不准增加时间容错
Nonce重复请求重放实现Nonce去重

八、总结

API签名验证是保护接口安全的重要手段:

  1. 选择合适算法:根据安全需求选择HMAC-SHA256
  2. 实现完整验证:时间戳、Nonce、签名缺一不可
  3. 配合其他措施:HTTPS、IP白名单、频率限制
  4. 做好密钥管理:使用安全的密钥存储方案

通过以上措施,可以有效防止接口被篡改和重放攻击。

http://www.jsqmd.com/news/874230/

相关文章:

  • 【火电机组、风能、储能】高比例风电电力系统储能运行及配置分析(Matlab代码实现)
  • 数据科学实践案例与项目管理
  • 大模型从0训练LLaMA全流程实战——基于昇腾910B集群
  • JWT令牌安全实践详解
  • AI系列【仅供参考】:周末用笔记本搞点大事:手把手教学部署 1.5、7B 版本 DeepSeek 智能助手
  • 黄仁勋放话:AI基建要烧掉4万亿美元 谁买单?
  • LeetCode 930:和相同的二元子数组 | 前缀和与哈希表
  • 从微服务到 Agent 服务:架构思维的迁移
  • 微服务安全防护实战:OAuth2与JWT鉴权
  • 【带RL负载的全波桥式整流器】功能齐全的单相非控整流器(Simulink)
  • 运维系列虚拟化系列OpenStack系列【仅供参考】:创建 VXLAN - 每天5分钟玩转 OpenStack(111)部署 instance 到 VXLAN - 每天5分钟玩转 OpenSt
  • LeetCode 1314:矩阵区域和 | 二维前缀和
  • 3分钟解决Mac与Windows文件交换难题:Nigate免费NTFS读写工具完全指南
  • 吴恩达:2026年是AI的黄金时代?普通人如何抓住最后上车窗口?
  • 3分钟搞定Windows桌面整理:NoFences免费开源工具终极指南
  • AI Agent Harness Engineering 在房地产中的应用:智能推荐与价值评估
  • 敏感数据加密存储实战
  • 通过 TaoToken 用量分析功能优化模型选型与调用策略
  • SLAM技术路线收敛?不,多模态融合正在重启路线之争
  • 前缀和与差分进阶总结 | 技巧归纳与实战应用
  • Go语言CI/CD流水线实践
  • 【GO context 】上下文取消/超时的本质
  • 无语,Trae的AI编程想混过去啊,我就说了点重话:我只要结果,我需要一个成语接龙程序,这个程序能正确运行,可以通过验收!
  • 2026第三方配送平台选型指南:成都本地跑腿加盟/成都本地配送平台/成都第三方配送平台/成都聚合配送平台/成都自配送平台/选择指南 - 优质品牌商家
  • 2026泳池设计优质厂家推荐:泳池设计/洗浴厂家/洗浴工程/洗浴改造/洗浴施工/洗浴设备/温泉洗浴设计/游泳池改造/选择指南 - 优质品牌商家
  • 企业级条码处理方案:ZXing.Net在.NET生态中的架构实践与性能优化
  • 【Appium 系列】第18节-重试与容错 — 移动端测试的稳定性保障
  • 2026泳池建造厂家推荐:酒店洗浴、户外泳池、泳池工程、泳池水处理、泳池设备、洗浴厂家、洗浴工程、洗浴改造、洗浴施工选择指南 - 优质品牌商家
  • 锌钢护栏网技术解析:四川公路铁路护栏网、四川双边丝护栏网、四川围栏网、四川学校球场围栏、四川市政道路护栏网、四川牛栏围栏网选择指南 - 优质品牌商家
  • 2026年Q2四川应急物资厂家评测:应急消防设备厂家/应急物资厂家电话/抗洪抢险应急设备/消防工具厂家/消防智能设备/选择指南 - 优质品牌商家