时间戳 + 签名机制
对于无需登录的门户网站接口,既要避免固定 token 被滥用,又要实现动态鉴权,同时不影响用户体验(无需强制登录),可以采用以下几种鉴权方案,结合 “动态可变” 和 “轻量化” 特点设计:
一、时间戳 + 签名机制(推荐)
核心思路
客户端每次请求时,基于当前时间戳、请求参数、双方约定的密钥生成一个动态签名,服务端验证签名合法性和时间戳有效性(防止重放攻击)。
签名每次请求都不同(因时间戳变化),且无法伪造(依赖密钥),无需登录即可实现动态鉴权。
实现步骤
-
客户端生成请求参数:
- 固定参数:
timestamp
(当前时间戳,精确到秒,如1620000000
)、nonce
(随机字符串,如 UUID,防止重复请求)。 - 业务参数:接口所需的实际数据(如
page=1&size=10
)。 - 签名生成:将
timestamp + nonce + 业务参数(排序后)+ 密钥
拼接成字符串,通过 MD5 或 SHA256 加密生成sign
。
示例拼接规则(按参数名 ASCII 排序,避免顺序影响签名):
key=密钥&nonce=xxx&page=1&size=10×tamp=1620000000
→ 加密后得到sign
。 - 固定参数:
-
客户端请求:
将timestamp
、nonce
、sign
作为请求头(或参数)发送,例如:http
GET /api/portal/data HTTP/1.1 timestamp: 1620000000 nonce: 5f4dcc3b5aa765d61d8327deb882cf99 sign: a1b2c3d4e5f6...
-
服务端验证:
- 验证
timestamp
是否在有效时间窗口内(如 ±5 分钟,防止过期请求被重放)。 - 验证
nonce
是否重复(可暂存最近 5 分钟的nonce
到缓存,防止重复使用)。 - 按客户端相同规则重新计算
sign
,与请求中的sign
比对,一致则通过。
- 验证
优势
- 签名动态变化(依赖时间戳和随机数),无法复用。
- 无需登录,不影响用户体验。
- 防重放(时间戳 + nonce)、防篡改(签名验证)。
注意
- 密钥需在客户端(前端)加密存储(如通过混淆、环境变量注入),避免明文暴露。
- 采用 HTTPS 传输,防止参数被中间人截取。
以下是基于 “时间戳 + 签名机制” 的完整实现,包括后端拦截器和前端 JS 请求示例,确保前后端签名逻辑一致。
一、后端拦截器实现(Spring Boot)
1. 签名工具类(核心,用于生成和验证签名)
java
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.util.*;/*** 签名工具类:生成签名和验证签名*/
public class SignUtil {// 密钥(实际项目中放在配置文件,前后端保持一致)public static final String SECRET_KEY = "your_portal_secret_key"; // 建议通过配置注入/*** 生成签名* @param method 请求方法(GET/POST等)* @param url 请求路径(如/api/data)* @param params 请求参数(Query参数或Body参数)* @param timestamp 时间戳(秒)* @param nonce 随机字符串* @return 签名串(SHA256加密)*/public static String generateSign(String method, String url, Map<String, Object> params, long timestamp, String nonce) {// 1. 拼接参数:按参数名ASCII排序,避免顺序影响签名TreeMap<String, Object> sortedParams = new TreeMap<>(params);StringBuilder paramStr = new StringBuilder();sortedParams.forEach((k, v) -> paramStr.append(k).append("=").append(v).append("&"));if (paramStr.length() > 0) {paramStr.deleteCharAt(paramStr.length() - 1); // 移除最后一个&}// 2. 拼接签名源串:method + url + params + timestamp + nonce + secretString source = String.format("%s%s%s%d%s%s",method.toUpperCase(),url,paramStr,timestamp,