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

Spring Boot实现接口时间戳鉴权

Spring Boot实现接口时间戳鉴权,签名(sign)和时间戳(ts)放入请求头(Header)。

一、请求头参数设计

参数名类型说明
tsLong13位时间戳(Unix毫秒值),必填,标识请求有效期
signString签名值,必填,用于校验请求合法性

二、签名算法调整(Header版)

**1. 构造原始字符串 **
str = key + url_encode(path) + ts
  • path:请求的URL路径部分(不含查询参数和域名),例如:/api/v1/user
  • url_encode(path):需对路径中的特殊字符进行URL编码(如中文、/等无需编码,但空格需转义为%20)。
2. 生成签名 SIGN
sgin = md5(S).toLowerCase()
  • 结果转换为小写字符串,与请求头中的sign对比校验。

三、Spring Boot实现流程

1. 创建拦截器(Interceptor)

用于拦截请求,校验tssign的合法性。

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Objects;public class AuthInterceptor implements HandlerInterceptor {private final String secretKey; // 从配置文件获取密钥,例如application.propertiesprivate final long maxClockSkew = 60 * 1000; // 时间窗口:60秒(允许客户端与服务端的时间误差)public AuthInterceptor(String secretKey) {this.secretKey = secretKey;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 校验时间戳是否存在String ts = request.getHeader("ts");if (StringUtils.isEmpty(ts)) {response.setStatus(HttpServletResponse.SC_BAD_REQUEST);response.setContentType("application/json");// 返回错误信息:缺少时间戳return false;}// 2. 校验时间戳格式及有效性long ts;try {ts = Long.parseLong(ts );} catch (NumberFormatException e) {response.setStatus(HttpServletResponse.SC_BAD_REQUEST);// 返回错误信息:时间戳格式错误return false;}long currentTime = System.currentTimeMillis();if (currentTime - ts > maxClockSkew|| ts - currentTime > maxClockSkew) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);// 返回错误信息:请求已过期return false;}// 3. 校验签名是否存在String sign= request.getHeader("sign");if (StringUtils.isEmpty(sign)) {response.setStatus(HttpServletResponse.SC_BAD_REQUEST);// 返回错误信息:缺少签名return false;}// 4. 构造原始签名字符串并生成签名String path = request.getRequestURI(); // 获取路径,例如"/api/v1/user"String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString()).replace("+", "%20") // 处理空格编码差异(URLEncoder默认用+,此处统一为%20).replace("%7E", "~"); // 保留波浪线~的原始格式String rawString = secretKey + encodedPath + tsHeader;try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] digest = md.digest(rawString.getBytes(StandardCharsets.UTF_8));StringBuilder sb = new StringBuilder();for (byte b : digest) {sb.append(String.format("%02x", b)); // 转换为小写16进制字符串}String generatedSign = sb.toString();// 5. 对比签名if (!generatedSign.equals(sign)) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);// 返回错误信息:签名校验失败return false;}} catch (Exception e) {response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);return false;}return true; // 校验通过,允许请求继续}
}
2. 配置拦截器(WebMvcConfigurer)

将拦截器注册到Spring Boot的拦截器链中,指定需要鉴权的接口路径。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {private final String secretKey; // 从配置文件获取密钥,例如通过@Value注入public WebConfig(String secretKey) {this.secretKey = secretKey;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {AuthInterceptor authInterceptor = new AuthInterceptor(secretKey);registry.addInterceptor(authInterceptor).addPathPatterns("/api/v1/**") // 需要鉴权的接口路径(如/api/v1下所有接口).excludePathPatterns("/api/v1/public/**"); // 无需鉴权的公共接口(可选)}
}
3. 配置文件(application.properties)
# 密钥(建议从环境变量或配置中心获取,而非硬编码)
auth.secret-key=your_secret_key_here

四、客户端请求示例(以Postman为例)

1. 请求头参数
名称
ts1686048000000(当前时间戳,13位Long)
sign计算得到的签名值(如e10adc3949ba59abbe56e057f20f883e
2. 签名计算步骤(JavaScript示例)
function generateSignature(key, path, ts) {const encodedPath = encodeURIComponent(path).replace(/[!'()*]/g, encodeURIComponent); // 严格编码特殊字符const rawString = key + encodedPath + ts;const md5 = require('crypto-js/md5'); // 需要安装crypto-js库return md5(rawString).toString().toLowerCase();
}// 示例调用
const key = 'your_secret_key';
const path = '/api/v1/user'; // 接口路径
const ts = Date.now(); // 当前时间戳(13位)
const sign = generateSignature(key, path, ts);
console.log(sign); // 输出签名值
http://www.xdnf.cn/news/903565.html

相关文章:

  • ABAP设计模式之---“简单设计原则(Simple Design)”
  • Windows无限期暂停更新
  • 模板方法模式:优雅封装不变,灵活扩展可变
  • web3-基于贝尔曼福特算法(Bellman-Ford )与 SMT 的 Web3 DeFi 套利策略研究
  • 贝叶斯深度学习!华科大《Nat. Commun.》发表BNN重大突破!
  • Science Robotics:UCLA 贺曦敏团队综述自主软体机器人
  • dexcap升级版之DexWild——面向户外环境的灵巧手交互策略:人类和机器人演示协同训练(人类直接带上动捕手套采集数据)
  • 【Linux 学习计划】-- 简易版shell编写
  • 【大模型LLM学习】Flash-Attention的学习记录
  • 阿里140 补环境日志
  • 华为 “一底双长焦” 专利公布,引领移动影像新变革
  • Caliper 负载(Workload)详细解析
  • 【NLP中向量化方式】序号化,亚编码,词袋法等
  • MySQL数据库基础(二)———数据表管理
  • 安卓基础(生成APK)
  • React 第五十六节 Router 中useSubmit的使用详解及注意事项
  • next,react封装axios,http请求
  • ✅ 常用 Java HTTP 客户端汇总及使用示例
  • 【零基础 快速学Java】韩顺平 零基础30天学会Java[学习笔记]
  • HTTP 请求协议简单介绍
  • 2025年SEVC SCI2区,潜力驱动多学习粒子群算法PDML-PSO,深度解析+性能实测
  • MySQL查询语句(续)
  • uniapp Vue2 获取电量的独家方法:绕过官方插件限制
  • Amazon Bedrock 助力 SolveX.AI 构建智能解题 Agent,打造头部教育科技应用
  • 当丰收季遇上超导磁测量:粮食产业的科技新征程
  • 智能手表健康监测系统的PSRAM存储芯片CSS6404LS-LI—高带宽、耐高温、微尺寸的三重突破
  • 微算法科技(NASDAQ:MLGO)基于信任的集成共识和灰狼优化(GWO)算法,搭建高信任水平的区块链网络
  • Guava LoadingCache 使用指南
  • Web前端基础:HTML-CSS
  • D3ctf-web-d3invitation单题wp