API项目3:API签名认证
问题引入
我们为开发者提供了接口,却对调用者一无所知
假设我们的服务器只能允许 100 个人同时调用接口。如果有攻击者疯狂地请求这个接口,那是很危险的。一方面这可能会损害安全性,另一方面耗尽服务器性能,影响正常用户的使用。
因此我们必须为接口设置保护措施,例如限制每个用户每秒只能调用十次接口,即实施请求频次的限额控制。所以我们必须知道谁在调用接口,并且不能让无权限的人随意调用。
在我们之前开发后端时,我们会进行一些权限检查。例如,当管理员执行删除操作时,后端需要检查这个用户是否为管理员,直接从后端的 session 中获取的。但问题来了,比如我是前端直接发起请求,没有登录操作,没有输入用户名和密码,我怎么去调用呢?
API 签名认证
API 签名认证过程
签发签名 -> 使用签名或校验签名
为什么需要API签名认证
为了保证安全性,不能让任何人都能调用接口。
适用于无需保存登录态的场景。只认签名,不关注用户登录态(为了更通用)。
如何在后端实现签名认证?
需要两个东西,即 accessKey 和 secretKey,来标识用户
和用户名和密码类似,不过每次调用接口都需要带上,实现无状态的请求。
“无状态”指的是每个请求都是独立的,服务器不会保存客户端的任何状态信息。每次请求都包含所有必要的信息来完成该请求。这种设计使得系统更易于扩展和管理。
签发 accessKey 和 secretKey
一般来说,accessKey 和 secretKey 需要尽可能复杂无规律,防止黑客尝试破解,特别是密码。
签名认证实现
通过 http request header 头传递参数。
- 参数 1:accessKey:调用的标识 userA, userB(复杂、无序、无规律)
- 参数 2:secretKey:密钥(复杂、无序、无规律)该参数不能放到请求头中
- 参数 3:用户请求参数
- 参数 4:签名
加密方式:
对称加密、非对称加密、md5 签名(不可解密)
用户参数 + 密钥 => 签名生成算法(MD5、HMac、Sha1) => 不可解密的值
怎么知道这个签名对不对?
服务端用一模一样的参数和算法去生成签名,只要和用户传的的一致,就表示一致。
怎么防重放?
- 参数 5:加 nonce 随机数,只能用一次,(存在问题:服务端要保存使用过的随机数)
所以配合
- 参数 6:加 timestamp 时间戳,校验时间戳是否过期。
API 签名认证是一个很灵活的设计,具体要有哪些参数、参数名如何一定要根据场景来。
为什么需要两个 key?
如果仅凭一个 key 就可以调用接口,那么任何拿到这个 key 的人都可以无限制地调用这个接口。这就好比,为什么你在登录网站时需要输入密码,而不是只输入用户名就可以了?其实这两者的原理是一样的。如果像 token 一样,一个 key 不行吗?token 本质上也是不安全的,有可能会通过重放等等方式来攻破的。
TODO:关于 accessKey、secretKey 的生成方法,自行编写代码实现
实践:
在客户端 拿到 accessKey、secretKey

需要获取用户传递的 accessKey 和 secretKey。
对于这种数据,建议不要直接在 URL 中传递,而是选择在请求头中传递会更为妥当。
因为 GET 请求的 URL 存在最大长度限制,如果你传递的其他参数过多,可能会导致关键数据被挤出。
@PostMapping("/user")
public String getUserNameByPost(@RequestBody User user, HttpServletRequest request) {// 从请求头中获取名为 "accessKey" 的值String accessKey = request.getHeader("accessKey");// 从请求头中获取名为 "secretKey" 的值String secretKey = request.getHeader("secretKey");// 如果 accessKey 不等于 "sujie" 或者 secretKey 不等于 "abcdefgh"if (!accessKey.equals("sujie") || !secretKey.equals("abcdefgh")){// 抛出一个运行时异常,表示权限不足throw new RuntimeException("无权限");}// 如果权限校验通过,返回 "POST 用户名字是" + 用户名return "POST 用户名字是" + user.getUsername();
}
改造一下 SuApiClient.java,发请求可以带上 header,用这个就可以去添加很多的请求头
// 使用POST方法向服务器发送User对象,并获取服务器返回的结果public String getUserNameByPost(@RequestBody User user) {// 将User对象转换为JSON字符串String json = JSONUtil.toJsonStr(user);// 使用HttpRequest工具发起POST请求,并获取服务器的响应HttpResponse httpResponse = HttpRequest.post("http://localhost:8123/api/name/user")// 添加前面构造的请求头.addHeaders(getHeaderMap()).body(json) // 将JSON字符串设置为请求体.execute(); // 执行请求// 打印服务器返回的状态码System.out.println(httpResponse.getStatus());// 获取服务器返回的结果String result = httpResponse.body();// 打印服务器返回的结果System.out.println(result);// 返回服务器返回的结果return result;}// 创建一个私有方法,用于构造请求头private Map<String, String> getHeaderMap() {// 创建一个新的 HashMap 对象Map<String, String> hashMap = new HashMap<>();// 将 "accessKey" 和其对应的值放入 map 中hashMap.put("accessKey", accessKey);// 将 "secretKey" 和其对应的值放入 map 中hashMap.put("secretKey", secretKey);// 返回构造的请求头 mapreturn hashMap;}
安全传递
存在的问题:
我们的请求有可能被人拦截,我们将密码放在请求头中,如果有中间人拦截到了你的请求,他们就可以直接从请求头中获取你的密码,然后使用你的密码发送请求。
密码绝对不能传递。也就是说,在向对方发送请求时,密码绝对不能以明文的方式传递,必须通过特殊的方式进行传递。
我们需要对该密码进行加密,这里通常称之为签名。
可以将用户传递的参数与该密钥拼接在一起,然后使用单向签名算法进行加密。
如何防止重放请求有两种方式可以考虑:
第一种方式是通过加入一个随机数实现标准的签名认证。每次请求时,发送一个随机数给后端。后端只接受并认可该随机数一次,一旦随机数被使用过,后端将不再接受相同的随机数。这种方式解决了请求重放的问题,因为即使对方使用之前的时间和随机数进行请求,后端会认识到该请求已经被处理过,不会再次处理。然而,这种方法需要后端额外开发来保存已使用的随机数。并且,如果接口的并发量很大,每次请求都需要一个随机数,那么可能会面临处理百万、千万甚至亿级别请求的情况。因此,除了使用随机数之外,我们还需要其他机制来定期清理已使用的随机数。
第二种方式是加入一个时间戳(timestamp)。每个请求在发送时携带一个时间戳,并且后端会验证该时间戳是否在指定的时间范围内,例如不超过10分钟或5分钟。这可以防止对方使用昨天的请求在今天进行重放。通过这种方式,我们可以一定程度上控制随机数的过期时间。因为后端需要同时验证这两个参数,只要时间戳过期或随机数被使用过,后端会拒绝该请求。因此,时间戳可以在一定程度上减轻后端保存随机数的负担。通常情况下,这两种方法可以相互配合使用。
因此,在标准的签名认证算法中,建议至少添加以下五个参数:accessKey、secretKey、sign、nonce(随机数)、timestamp(时间戳)。此外,建议将用户请求的其他参数,例如接口中的 name 参数,也添加到签名中,以增加安全性。
安全传递实现
新建一个 utils 包,在 utils 包下新建 SignUtils.java(签名工具)
这个 hashmap 还需要进行拼接,我们传递的是用户的这些参数,但其实没有必要传递那么多参数,直接将 body 作为参数传递进来(在这里,我们也可以传递 hashmap,只要有一些共同的参数,能让客户端和服务端之间保持一致即可)。
/*** 签名工具*/
public class SignUtils {/*** 生成签名* @param hashMap 包含需要签名的参数的哈希映射* @param secretKey 密钥* @return 生成的签名字符串*/public static String genSign(Map<String, String> hashMap, String secretKey) {// 使用SHA256算法的DigesterDigester md5 = new Digester(DigestAlgorithm.SHA256);// 构建签名内容,将哈希映射转换为字符串并拼接密钥String content = hashMap.toString() + "." + secretKey;// 计算签名的摘要并返回摘要的十六进制表示形式return md5.digestHex(content);}
}
刚刚客户端只有这两个参数 accessKey、secretKey
现在再加几个参数
/*** 获取请求头的哈希映射* @param body 请求体内容* @return 包含请求头参数的哈希映射*/
private Map<String, String> getHeaderMap(String body) {Map<String, String> hashMap = new HashMap<>();hashMap.put("accessKey", accessKey);// 注意:不能直接发送密钥// hashMap.put("secretKey", secretKey);// 生成随机数(生成一个包含100个随机数字的字符串)hashMap.put("nonce", RandomUtil.randomNumbers(4));// 请求体内容hashMap.put("body", body);// 当前时间戳// System.currentTimeMillis()返回当前时间的毫秒数。通过除以1000,可以将毫秒数转换为秒数,以得到当前时间戳的秒级表示// String.valueOf()方法用于将数值转换为字符串。在这里,将计算得到的时间戳(以秒为单位)转换为字符串hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));// 生成签名hashMap.put("sign", genSign(body, secretKey));return hashMap;
}/*** 通过POST请求获取用户名* @param user 用户对象* @return 从服务器获取的用户名*/
public String getUserNameByPost(@RequestBody User user) {// 将用户对象转换为JSON字符串String json = JSONUtil.toJsonStr(user);HttpResponse httpResponse = HttpRequest.post("http://localhost:8123/api/name/user")// 添加请求头.addHeaders(getHeaderMap(json))// 设置请求体.body(json)// 发送POST请求.execute();// 打印响应状态码System.out.println(httpResponse.getStatus());// 打印响应体内容String result = httpResponse.body();System.out.println(result);return result;
}
接下来服务端
@PostMapping("/user")public String getUserNameByPost(@RequestBody User user, HttpServletRequest request) {// 1.拿到这五个我们可以一步一步去做校验,比如 accessKey 我们先去数据库中查一下// 从请求头中获取参数String accessKey = request.getHeader("accessKey");String nonce = request.getHeader("nonce");String timestamp = request.getHeader("timestamp");String sign = request.getHeader("sign");String body = request.getHeader("body");// 不能直接获取秘钥// String secretKey = request.getHeader("secretKey");// TODO 2.校验权限,这里模拟一下,直接判断 accessKey 是否为"yupi",实际应该查询数据库验证权限if (!accessKey.equals("sujie")){throw new RuntimeException("无权限");}// TODO 3.校验一下随机数,因为时间有限,就不带大家再到后端去存储了,后端存储用hashmap或redis都可以// 校验随机数,模拟一下,直接判断nonce是否大于10000if (Long.parseLong(nonce) > 10000) {throw new RuntimeException("无权限");}// TODO 4.校验时间戳与当前时间的差距,交给大家自己实现//// if (timestamp) {}// TODO 5. 从实际数据库中取得用户secretKeyString serverSign = SignUtils.genSign(body, "abcdefgh");if (!serverSign.equals(sign)) {throw new RuntimeException("无权限");}return "POST 用户名字是" + user.getUsername();}
整个签名认证算法的流程就是这样
需要强调的是,API签名认证是一种非常灵活的设计,具体需要哪些参数以及参数名的选择都应根据具体场景来确定。尽量避免在前端进行签名认证,而是由服务端来处理
例如,某些公司或项目的签名认证可能会包含 userId 字段以区分用户。还可能包含 appId 和 version 字段来表示应用程序的版本号。有时还会添加一些固定的盐值等等。
相关文章:
API项目3:API签名认证
问题引入 我们为开发者提供了接口,却对调用者一无所知 假设我们的服务器只能允许 100 个人同时调用接口。如果有攻击者疯狂地请求这个接口,那是很危险的。一方面这可能会损害安全性,另一方面耗尽服务器性能,影响正常用户的使用。…...
unity学习-Directional light光的设置
ccColor:环境光的颜色 Mode:灯光模式,Realtime(实时光影),实时计算光影,消耗性能但是效果好,Baked烘焙光影,将光的照射效果作为贴图贴在静态的物体上形成一种虚假的光照…...
简单实现通过电脑操作手机
通过电脑操作手机,支持单击,拖抓事件,延时有1-2秒。 具体步骤: 1、从手机截图到sdcard 2、将图片导出到PC 3、从PC加载展示图片 4、开启定时器 5、设置点击、滚动事件 1、 private static void takeScreenshot(String path)…...
基于ESP32的便携式游戏机
基于ESP32的便携式游戏机 一、项目说明二、项目材料三、程序测试四、设置LCD屏幕五、控制设置六、测试电路七、外壳制作八、结果 视频: ESP32 pro 一、项目说明 欢迎来到复古游戏的世界!你是否曾经想要以便携格式重温童年的经典游戏?在这个…...
【LeetCode 88. 合并两个有序数组】 java实现
LeetCode 88. 合并两个有序数组 题目描述 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 的大小等于 m n(即…...
200Kg大载重多旋无人机价格高昂技术分析
200Kg大载重多旋无人机作为一种高度专业化的航空工具,其价格相较于普通无人机显著较高,这主要是由于其在技术设计和生产过程中所需的高要求所致。以下是对其价格高昂的技术分析: 一、高性能材料与结构设计 1. 高强度轻量化材料:…...
快速理解http的get和post
在网络通信中,HTTP 协议扮演着非常重要的角色,而不同的 HTTP 方法决定了客户端与服务器之间的交互方式。 这里讲一下最常用的两种方法——GET 和 POST。 一、GET 方法 GET 方法用于从服务器获取资源。 这就像去图书馆借书——你向图书馆请求一本特定的…...
Mamba学习笔记(3)—S4原理基础
文章目录 Efficiently Modeling Long Sequences with Structured State Spaces0 Abstract1 Introduction2 Background:State Spaces2.1 State Space Models: A Continuous-time Latent State Model2.2 Addressing Long-Range Dependencies with HiPPO2.3 Discrete-t…...
好看的ppt字体推荐!分享3个制作幻灯片的常用软件!
ppt什么字体好看? 好看是一个比较主观的概念,见仁见智,在选用ppt字体时,比起关注好看,字体是否“合适”应该是优先级更高的需求。这里的合适,即PPT所选用字体的风格、呈现效果是否与PPT的主题和使用场景相…...
第6篇:无线与移动网络
目录 引言 6.1 无线网络的基础概念 6.2 无线局域网(WLAN)与IEEE 802.11 6.3 蓝牙与无线个域网(WPAN) 6.4 无线城域网(WMAN)与WiMax 6.5 ZigBee与智能家居 6.6 移动蜂窝网络(3G/4G/5G&…...
【C++标准模版库】unordered_map和unordered_set的介绍及使用
unordered_map和unordered_set 一.unordered_set1.unordered_set类的介绍2.unordered_set和set的使用差异 二.unordered_map1.unordered_map和map的使用差异 三.unordered_multimap/unordered_multiset四.unordered_map/unordered_set的哈希相关接口 一.unordered_set 1.unord…...
深度解析Transformer:从自注意力到MLP的工作机制
深度解析Transformer:从自注意力到MLP的工作机制 以下大部分内容本来自对3Blue1Brown的视频讲解的整理组织 一、Transformer的目标 为了简化问题,这里讨论的模型目标是接受一段文本,预测下一个词。这种任务对模型提出了两大要求:…...
《米小圈动画成语》|在趣味中学习,在快乐中掌握成语知识!
作为一名家长,我一直希望孩子能够在学习的过程中既感受到乐趣,又能获得真正的知识。成语作为中华文化的精华,虽然意义深远、简洁凝练,但对于一个小学生来说,学习和理解这些言简意赅的成语无疑是一个挑战。尤其是有些成…...
linux系统之jar启动脚本
编辑linux启动脚本 执行 vi run_blog 按i 进入编辑,复制以下代码,并根据当前环境修改三个参数。以下是详细完整脚本代码: #!/bin/bash# 配置部分 JAR_PATH"/path/to/your/app.jar" # 替换为你的 JAR 文件的实际路径 L…...
简单认识Maven 2-Maven坐标
Maven坐标 在 Maven 中,坐标(Coordinates)用于唯一标识一个项目或依赖项,就像在现实世界中通过经纬度来确定一个地理位置一样。Maven 坐标由三个主要部分组成:groupId、artifactId 和 version。 groupId(…...
Xilinx UltraScale系列FPGA纯verilog图像缩放,工程项目解决方案,提供2套工程源码和技术支持
目录 1、前言工程概述免责声明FPGA高端图像处理培训 2、相关方案推荐我这里已有的FPGA图像缩放方案本方案在Xilinx Artix7 系列FPGA上的应用本方案在Xilinx Kintex7 系列FPGA上的应用本方案在Xilinx Zynq7000 系列FPGA上的应用本方案在国产FPGA紫光同创系列上的应用本方案在国产…...
React(二) JSX中的this绑定问题;事件对象参数传递;条件渲染;列表渲染;JSX本质;购物车案例
文章目录 一、jsx事件绑定1. 回顾this的绑定方式2. jsx中的this绑定问题(1) 方式一:bind绑定(2) 方式二:使用 ES6 class fields 语法(3) 方式三:直接传入一个箭头函数(重要) 3.事件参数传递(1) 传递事件对象event(2) 传递其他参数 4. 事件绑定…...
前端开发攻略---取消已经发出但是还未响应的网络请求
目录 注意: 1、Axios实现 2、Fetch实现 3、XHR实现 注意: 当请求被取消时,只会本地停止处理此次请求,服务器仍然可能已经接收到了并处理了该请求。开发时应当及时和后端进行友好沟通。 1、Axios实现 <!DOCTYPE html> &…...
韩信走马分油c++
韩信走马分油c 题目算法代码 题目 把油桶里还剩下的10斤油平分,只有一个能装3斤的油葫芦和一个能装7斤的瓦罐。如何分。 算法 油壶编号0,1,2。不同倒法有:把油从0倒进0(本壶到本壶,无效)&…...
【Linux】Anaconda下载安装配置Pytorch安装配置(保姆级)
目录 Anaconda下载 Anaconda安装 conda init conda --v Conda 配置 conda 环境创建 conda info --envs conda list Pytorch安装配置 检验安装情况 检验是否可以使用GPU Anaconda下载 可以通过两种途径完成Anaconda安装包的下载 途径一:本地windows下…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
