Spring Boot对访问密钥加解密——HMAC-SHA256
HMAC-SHA256 简介
HMAC-SHA256 是一种基于 哈希函数 的消息认证码(Message Authentication Code, MAC),它结合了哈希算法(如 SHA-256)和一个密钥,用于验证消息的完整性和真实性。
HMAC 是 “Hash-based Message Authentication Code” 的缩写,它广泛应用于网络通信中,用于保证消息在传输过程中未被篡改,同时也可以校验消息是否来自可信方。
HMAC-SHA256 的组成
HMAC-SHA256 的运算公式如下:
HMAC(key, message) = SHA256((key ⊕ opad) || SHA256((key ⊕ ipad) || message))
-
key:一个秘密密钥,用于认证消息,只有通信双方知道。 -
message:需要进行签名的消息内容。 -
SHA256:一种安全的哈希函数,用于生成固定长度的摘要值。 -
⊕:按位异或操作(XOR)。 -
opad和ipad:
opad:外部填充字节 (0x5c)。ipad:内部填充字节 (0x36)。
流程解释:
- 将密钥与
ipad结合,并将其与消息一起进行第一次哈希。 - 将密钥与
opad结合,并将其与第一次哈希的结果一起进行第二次哈希。 - 最终输出 HMAC 值。
HMAC-SHA256 的特点
- 基于密钥:
- 与普通的 SHA256 不同,HMAC-SHA256 引入了密钥,只有通信双方知道密钥,保证了消息的认证性。
- 抗篡改:
- 如果消息在传输过程中被篡改,HMAC 认证将失败。
- 抗重放攻击:
- 通常结合时间戳(
timestamp)或随机数(nonce),防止消息被恶意重放。
- 通常结合时间戳(
- 性能高效:
- 基于哈希函数的算法速度快,适合高并发场景。
- 简单易用:
- 常用于 REST API、签名验证和网络协议中。
HMAC-SHA256 的应用场景
- API 鉴权:
- 许多云服务和 API 平台(如 AWS、阿里云等)使用 HMAC-SHA256 来验证 API 请求的真实性和完整性。
- 消息完整性校验:
- 用于验证消息在传输过程中未被篡改。
- 数据完整性校验:
- 用于验证文件或数据传输的完整性(结合密钥,防止恶意伪造)。
- 通信协议:
- 用于加密通信协议(如 TLS)的消息认证。
- Token 签名:
- 用于生成和验证基于密钥的 Token,如 JWT(JSON Web Token)的签名部分。
HMAC-SHA256 的优点
- 安全性强:
- 基于 SHA-256 的安全性,结合密钥使用,安全性更高。
- 对抗攻击:
- 有效抵抗暴力破解、哈希碰撞和中间人攻击。
- 跨平台支持:
- 各种编程语言和库都支持 HMAC-SHA256。
- 性能优越:
- 计算量小,适合高性能需求的场景。
HMAC 与其他加密算法的对比
| 特性 | HMAC-SHA256 | RSA 签名 | 普通哈希 |
|---|---|---|---|
| 是否需要密钥 | 是 | 是 | 否 |
| 加密/认证用途 | 认证 | 认证和加密 | 数据完整性校验 |
| 性能 | 高效 | 较慢 | 高效 |
| 应用场景 | API 鉴权、消息验证 | 数字签名、PKI | 文件校验、数据校验 |
常见问题
- HMAC-SHA256 与普通 SHA-256 有什么区别?
- SHA-256 是一种单向哈希算法,用于生成固定长度的摘要值;
- HMAC-SHA256 是基于 SHA-256 和密钥的消息认证码,除了生成摘要外,还能验证消息来源和完整性。
- HMAC-SHA256 的密钥如何管理?
- 密钥必须严格保密,通常存储在安全的配置文件或密钥管理服务(如 AWS KMS)中。
- 是否需要 HTTPS?
- HMAC-SHA256 可以防止消息篡改,但不能保护消息的机密性。建议配合 HTTPS 使用。
- 可以用 HMAC-SHA256 替代 RSA 签名吗?
- 如果双方共享密钥,HMAC-SHA256 是更高效的选择;
- 如果需要非对称签名或验证,应该使用 RSA。
代码示例
客户端示例
假设这是一个Java客户端(可能是后端服务或桌面应用等),要调用你的服务接口 /api/secure,并用 HMAC-SHA256 做签名。
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;public class HmacClientExample {public static void main(String[] args) throws Exception {// 1) 准备必要参数String accessKeyId = "myKeyId";String accessKeySecret = "myKeySecret"; // 保密String method = "POST";String path = "/api/secure";// 例如携带一个 timestamp (yyyyMMddHHmmss)String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));// 2) 如果有请求体,需要计算 bodyHash (这里只是示例)// 实际可对 JSON 字符串做 MD5 或 SHA256,再 Hex 或 Base64String requestBody = "{\"foo\":\"bar\"}"; // JSONString bodyHash = sha256Hex(requestBody);// 3) 拼装 StringToSign (示例逻辑,可自定义)// 这里用换行分隔 method, path, timestamp, bodyHashString stringToSign = method + "\n" + path + "\n" + timestamp + "\n" + bodyHash;// 4) 做 HMAC-SHA256String signature = hmacSha256Base64(stringToSign, accessKeySecret);// 5) 将签名和 keyId、timestamp 放到 HTTP 头部// 伪代码: 构建 HTTP 请求System.out.println("X-AccessKeyId: " + accessKeyId);System.out.println("X-Timestamp: " + timestamp);System.out.println("X-Signature: " + signature);// 之后再把 requestBody 当作 JSON 发出 (POST)// ...// 这是示例演示,真实项目中可用 HttpClient、OkHttp 等发请求}/*** 计算字符串的 SHA-256 再转 hex (可选:也可用 Base64)*/private static String sha256Hex(String data) throws Exception {MessageDigest digest = MessageDigest.getInstance("SHA-256");byte[] bytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));return bytesToHex(bytes);}/*** HMAC-SHA256 + Base64*/private static String hmacSha256Base64(String data, String secret) throws Exception {Mac mac = Mac.getInstance("HmacSHA256");SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");mac.init(keySpec);byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(rawHmac);}private static String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder(bytes.length * 2);for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}
}
- 核心:
StringToSign拼装 +HMAC-SHA256计算签名 + 在请求头中带上accessKeyId、timestamp、signature。- bodyHash 的计算方式可自行定义,也可以用 MD5、直接放明文 body 等。只要客户端和服务端保持一致即可。
服务端示例 (Spring Boot)
下面以 Spring Boot + Controller 为例,展示如何验证签名。主要逻辑:
- 从 HTTP 头中取
X-AccessKeyId,X-Timestamp,X-Signature。 - 根据
accessKeyId找到 secret; - 用相同的方式拼装
StringToSign; - 做同样的 HMAC-SHA256 计算;
- 比对与客户端传来的
signature是否相同。
2.1 Controller 示例
@RestController
@RequestMapping("/api")
public class SecureApiController {// 示例:内存中保存 keyId -> keySecret 映射private Map<String, String> keyStore = new HashMap<>();public SecureApiController() {// 假设这里初始化了一个myKeyId -> myKeySecretkeyStore.put("myKeyId", "myKeySecret");}@PostMapping("/secure")public ResponseEntity<?> secureEndpoint(HttpServletRequest request,@RequestBody(required=false) String body // raw JSON) {try {// 1) 从header读取String accessKeyId = request.getHeader("X-AccessKeyId");String timestamp = request.getHeader("X-Timestamp");String clientSignature = request.getHeader("X-Signature");if (accessKeyId == null || timestamp == null || clientSignature == null) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Missing auth headers");}// 2) 查找keySecretString keySecret = keyStore.get(accessKeyId);if (keySecret == null) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid accessKeyId");}// 3) 计算 bodyHash(可选)// 假设客户端用了 sha256Hex(body)String bodyHash = sha256Hex(body == null ? "" : body);// 4) 与客户端相同的拼接方式String method = request.getMethod(); // "POST"String path = request.getRequestURI(); // "/api/secure"// StringToSignString stringToSign = method + "\n" + path + "\n" + timestamp + "\n" + bodyHash;// 5) 服务端做 HMAC-SHA256String serverSignature = hmacSha256Base64(stringToSign, keySecret);// 6) 比对签名if (!serverSignature.equals(clientSignature)) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Signature mismatch");}// 7) 可选校验: timestamp 是否过期if (!checkTimestampValid(timestamp)) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Timestamp expired or invalid");}// 8) 一切正常return ResponseEntity.ok("Success! Request body was: " + body);} catch (Exception e) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Auth error: " + e.getMessage());}}// 计算 SHA256Hexprivate String sha256Hex(String data) throws Exception {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));return bytesToHex(digest);}// HMAC-SHA256 + Base64private String hmacSha256Base64(String data, String secret) throws Exception {Mac mac = Mac.getInstance("HmacSHA256");SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");mac.init(keySpec);byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(rawHmac);}private String bytesToHex(byte[] bytes) {StringBuilder sb = new StringBuilder(bytes.length * 2);for (byte b : bytes) {sb.append(String.format("%02x", b));}return sb.toString();}// 时间戳校验 (±15分钟示例)private boolean checkTimestampValid(String timestampStr) {try {// 这里假设 timestampStr 是 yyyyMMddHHmmssDateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");LocalDateTime reqTime = LocalDateTime.parse(timestampStr, fmt);LocalDateTime now = LocalDateTime.now();return !reqTime.isBefore(now.minusMinutes(15)) && !reqTime.isAfter(now.plusMinutes(15));} catch (Exception e) {return false;}}
}
- 注意:上面为了演示方便,用
@RequestBody(required=false) String body直接拿到原始 JSON 字符串,再做sha256Hex;如果是对象映射,你要注意读取流和计算摘要的先后顺序。- 你也可以在 Filter 或 Interceptor 里做这个签名验签逻辑,避免在每个 Controller 里写。
timestamp校验 + 可能的 nonce 防重放(可用 Redis 记录 5 分钟内出现过的(accessKeyId,timestamp,nonce)),以更好地防御重复调用。
总结
- 客户端
- 准备
accessKeyId,accessKeySecret; - 拼出
StringToSign(通常包含 method、path、timestamp、bodyHash 等); - HMAC-SHA256(
StringToSign,accessKeySecret) →signature; - 在 HTTP 请求头里带上
accessKeyId,signature,timestamp; - 用 JSON 作为请求体时,别忘了和服务端在 bodyHash 算法上保持一致。
- 准备
- 服务端
- 通过
accessKeyId找到对应的accessKeySecret; - 按同样规则构造
StringToSign; - 计算 HMAC-SHA256 并和客户端的
signature对比; - 一致则通过,不一致则 401/403;
- 可加时间戳、nonce、限流 等加强安全性。
- 通过
- 优点
- 不需要公钥/私钥,也不需要RSA 加解密;
- 计算速度快、实现相对简单;
- 通用性强,许多云厂商、API网关都采用类似 HMAC 签名模式。
- 注意
- 一定要保护好
accessKeySecret,客户端泄露就会被冒用。 - 使用 HTTPS 来保证传输安全,防止中间人截获签名或篡改。
- 若对大文件、流式上传等,需要在数据处理上稍作适配(hash可能需分段计算)。
- 一定要保护好
这套 HMAC-SHA256 签名鉴权就是在很多云服务(阿里云、AWS、腾讯云)都在用的模式。只要客户端与服务端约定好StringToSign 的拼装方式、accessKeyId → secret 映射、时间戳/nonce防重放,就能形成一套轻量、高效的对外接口鉴权机制。
相关文章:
Spring Boot对访问密钥加解密——HMAC-SHA256
HMAC-SHA256 简介 HMAC-SHA256 是一种基于 哈希函数 的消息认证码(Message Authentication Code, MAC),它结合了哈希算法(如 SHA-256)和一个密钥,用于验证消息的完整性和真实性。 HMAC 是 “Hash-based M…...
HTML 元素:网页构建的基础
HTML 元素:网页构建的基础 HTML(HyperText Markup Language,超文本标记语言)是构建网页的基石。它定义了网页的结构和内容,而HTML元素则是构成HTML文档的基石。在本篇文章中,我们将深入探讨HTML元素的概念、类型、用法,以及如何在网页设计中有效地使用它们。 什么是HT…...
HEIC 是什么图片格式?如何把 iPhone 中的 HEIC 转为 JPG?
在 iPhone 拍摄照片时,默认的图片格式为 HEIC。虽然 HEIC 格式具有高压缩比、高画质等优点,但在某些设备或软件上可能存在兼容性问题。因此,将 HEIC 格式转换为更为通用的 JPG 格式就显得很有必要。本教程将介绍如何使用简鹿格式工厂…...
爆肝1个月:DDR4 的信号完整性(万字长文SI)
前言: 大学里面,总有很多课程,很浪费时间,学了没点用处,问过老师,为什么信号完整性,示波器使用等课程不开呢,这种是对工作真实有帮助的? 老师:因为老师…...
前端js验证码插件
相关代码,在最上方的绑定资源...
关于easy-es对时间范围查询遇到的小bug
前言:在使用easy-es之前作为一个小白的我只有es原生查询的基础,在自己通过查看官方文档自学easy-es遇到了一个挫折,其他的还好语法和MybatisPlus差不多,正以为我觉得很快就能入手,在对时间范围的判断就给我当头一棒&am…...
Mask R-CNN
目录 摘要 Abstract Mask R-CNN 网络架构 Backbone RPN Proposal Layer ROIAlign bbox检测 Mask分割 损失计算 实验复现 总结 摘要 Mask R-CNN是在Faster R-CNN的基础上进行改进的目标检测和实例分割网络。Faster R-CNN主要用于目标检测,输出对象的边…...
大模型-Dify使用笔记
大模型-Dify使用笔记 0、调整docker镜像源1、安装1、Docker Compose方式部署 2、访问 Dify 0、调整docker镜像源 由于墙的存在,所以默认的docker镜像源很难拉取项目,需要调整相关的docker配置文件 vim /etc/docker/daemon.json添加如下docker镜像源 { …...
Suno Api V4模型无水印开发「综合实战开发自己的音乐网站」 —— 「Suno Api系列」第14篇
历史文章 Suno AI API接入 - 将AI音乐接入到自己的产品中,支持120并发任务 Suno Api V4模型无水印开发「灵感模式」 —— 「Suno Api系列」第1篇 Suno Api V4模型无水印开发「自定义模式」 —— 「Suno Api系列」第2篇 Suno Api V4模型无水印开发「AI生成歌词」…...
云原生架构中的中间件容器化:优劣势与实践探索
在云原生架构逐步推进的过程中,许多企业已经开始将应用和服务容器化,以充分利用云计算带来的弹性和自动化。随着容器技术的发展,容器化不仅仅限于应用层,越来越多的中间件也被考虑纳入容器化范畴,包括Redis、Kafka、Ra…...
如何测试模型推理性能:从零开始的Python指南
如何测试模型推理性能:从零开始的Python指南 什么是模型推理性能?测试模型推理性能的步骤1. 监测内存使用情况2. 测试模型吞吐量 运行测试总结 在机器学习和深度学习中,模型的推理性能是一个非常重要的指标。它可以帮助我们了解模型在实际应用…...
我们来学activiti -- bpmn
bpmn 题记bpmn结余 题记 在《Activiti很难学》提到学习知识点需要面对的思想钢印问题 按常见步骤,先展示下官方的客套话 BPMN(Business Process Model and Notation)是一种业务流程建模符号, 它是一种图形化的语言,用…...
【每日学点鸿蒙知识】节点析构问题、区分手机和pad、 Navigation路由问题、Tabs组件宽度、如何监听Map
1、HarmonyOS 只调用根节点的dispose,是否其下的子节点都能析构掉还是需要遍历子节点,都执行dispose才能正常析构? 前端持有引用关系的需要dispose,new出来的builderNode和FrameNode也需要dispose。只调用根节点的dispose,无法保证其下的子节…...
敏捷测试文化的转变
敏捷文化是敏捷测试转型的基础,只有具备敏捷文化的氛围,对组织架构、流程和相关测试实践的调整才能起作用。在前面的敏捷测试定义中,敏捷测试是遵从敏捷软件开发原则的一种测试实践,这意味着敏捷的价值观。 此外,从传…...
如何配置线程池参数,才能创建性能最好、最稳定的Spring异步线程池?
配置性能最好、最稳定的Spring异步线程池,需要综合考虑业务场景、硬件资源(CPU核心数、内存等)、并发量、任务特性(CPU密集型、IO密集型等)以及线程池参数。 以下是优化线程池配置的关键点及代码示例: 线程…...
【时间之外】IT人求职和创业应知【80】-特殊日子
目录 北京冬季招聘会 OpenAI CEO炮轰马斯克 英伟达推出全新AI芯片B300 莫欢喜,总成空。本周必须要谨行慎言。 感谢所有打开这个页面的朋友。人生不如意,开越野车去撒野,会害了自己,不如提升自己。提升自己的捷径就是学习和思考…...
Vue中接入萤石等直播视频(更新中ing)
一、萤石: 1. 萤石云开发文档: https://open.ys7.com/help/31 2、安装: npm install ezuikit-js --save 3、在文件中引用:import EZUIKit from ezuikit-js 4、具体代码: 获取accessToken:https://open.…...
如何学习、使用Ai,才能跟上时代的步伐?
目录 1. 打好基础:理解AI的核心概念 2. 学习AI的核心领域 3. 实践:动手做项目,积累经验 4. 利用AI工具提升工作效率 5. 培养AI思维与批判性思维 6. 关注AI领域的最新研究与趋势 7. 培养跨学科能力 总结: 在AI时代…...
RabbitMQ中的异步Confirm模式:提升消息可靠性的利器
在现代分布式系统中,消息队列(Message Queue)扮演着至关重要的角色,它能够解耦系统组件、提高系统的可扩展性和可靠性。RabbitMQ作为一款广泛使用的消息队列中间件,提供了多种机制来确保消息的可靠传递。其中ÿ…...
Linux(Centos 7.6)目录结构详解
Linux(Centos 7.6)是一个操作系统,其核心设计理念是将一切资源抽象为文件,即一切皆文件。比如系统中的硬件设备硬盘、网络接口等都被视为文件。Windows系统一般是分为C、D、E盘。而Linux(Centos 7.6)是以斜线"/"作为文件系统的开始目录&#x…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
