hua - api
type: channel
link: https://lijunhua.cn/tag/api/
items: 基于 HMAC 的 API 接口安全认证实现

基于 HMAC 的 API 接口安全认证实现
type: item
date: 2026-03-17T10:20:00+08:00
description: 核心设计思路安全认证方案主要解决以下几个核心问题：防重放攻击：通过时间戳和随机 Nonce 值限制请求的有效期防请求篡改：通过 HMAC 签名验证请求参数的完整性防时序攻击：使用安全的字符串比较方式验证令牌密钥安全：通过密钥派生函数 (KDF) 增强原始密钥的安全性核心代码实现与解析数据结构定义首先定义存储令牌信息的AuthToken类，包含时间戳、随机数和签名令牌三个核心字段：public static class AuthToken { private final long timestamp; // 时间戳(秒级) private final String nonce; // 随机数 private final String token; // 签名令牌 public AuthToken(long timestamp, String nonce, String token) { this.timestamp = timestamp; this.nonce = nonce; this.token = token; } // Getter方法... }核心配置参数定义认证方案的核心配置，可根据业务需求调整：// 令牌有效期(5分钟) private static final long TIME_WINDOW_SECONDS = TimeUnit.MINUTES.toSeconds(5); // 加密算法 private static final String HMAC_ALGORITHM = "HmacSHA256"; // HKDF盐值和信息字段(增强密钥派生安全性) private static final String HKDF_SALT = "HKDF_SALT"; private static final String HKDF_INFO = "HKDF_INFO"; // 随机数长度(16字节) private static final int NONCE_LENGTH = 16;令牌生成逻辑generateToken方法是整个认证方案的核心，负责生成包含时间戳、随机数和签名的完整令牌：public static AuthToken generateToken(String clientId) { // 1. 获取当前秒级时间戳 long timestamp = Instant.now().getEpochSecond(); // 2. 生成随机Nonce值(防止重放攻击) String nonce = generateNonce(); // 3. 构建签名原文：时间戳_随机数_客户端ID String message = String.join("_", String.valueOf(timestamp), nonce, clientId != null ? clientId : "" ); // 4. 密钥派生：通过双重HMAC增强密钥安全性 byte[] derivedKey = deriveKey((clientId != null ? clientId : "").getBytes(StandardCharsets.UTF_8)); // 5. 计算HMAC-SHA256签名 String token = hmacDigest(derivedKey, message); return new AuthToken(timestamp, nonce, token); }密钥派生函数为了提升密钥安全性，我们实现了基于 HKDF 思想的密钥派生函数，通过双重 HMAC 处理原始密钥：private static byte[] deriveKey(byte[] inputKey) { try { // 第一轮HMAC：使用盐值处理原始密钥 Mac hmac1 = Mac.getInstance(HMAC_ALGORITHM); SecretKeySpec keySpec1 = new SecretKeySpec(HKDF_SALT.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM); hmac1.init(keySpec1); byte[] tempKey = hmac1.doFinal(inputKey); // 第二轮HMAC：使用临时密钥处理信息字段 Mac hmac2 = Mac.getInstance(HMAC_ALGORITHM); SecretKeySpec keySpec2 = new SecretKeySpec(tempKey, HMAC_ALGORITHM); hmac2.init(keySpec2); return hmac2.doFinal(…