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

API项目3:API签名认证

问题引入

我们为开发者提供了接口,却对调用者一无所知

假设我们的服务器只能允许 100 个人同时调用接口。如果有攻击者疯狂地请求这个接口,那是很危险的。一方面这可能会损害安全性,另一方面耗尽服务器性能,影响正常用户的使用。

因此我们必须为接口设置保护措施,例如限制每个用户每秒只能调用十次接口,即实施请求频次的限额控制。所以我们必须知道谁在调用接口,并且不能让无权限的人随意调用。

在我们之前开发后端时,我们会进行一些权限检查。例如,当管理员执行删除操作时,后端需要检查这个用户是否为管理员,直接从后端的 session 中获取的。但问题来了,比如我是前端直接发起请求,没有登录操作,没有输入用户名和密码,我怎么去调用呢?

API 签名认证

API 签名认证过程

签发签名  -> 使用签名或校验签名

为什么需要API签名认证

为了保证安全性,不能让任何人都能调用接口。

适用于无需保存登录态的场景。只认签名,不关注用户登录态(为了更通用)。

如何在后端实现签名认证?

需要两个东西,即 accessKeysecretKey,来标识用户

和用户名和密码类似,不过每次调用接口都需要带上,实现无状态的请求。

“无状态”指的是每个请求都是独立的,服务器不会保存客户端的任何状态信息。每次请求都包含所有必要的信息来完成该请求。这种设计使得系统更易于扩展和管理。

签发 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签名认证

问题引入 我们为开发者提供了接口&#xff0c;却对调用者一无所知 假设我们的服务器只能允许 100 个人同时调用接口。如果有攻击者疯狂地请求这个接口&#xff0c;那是很危险的。一方面这可能会损害安全性&#xff0c;另一方面耗尽服务器性能&#xff0c;影响正常用户的使用。…...

unity学习-Directional light光的设置

ccColor&#xff1a;环境光的颜色 Mode&#xff1a;灯光模式&#xff0c;Realtime&#xff08;实时光影&#xff09;&#xff0c;实时计算光影&#xff0c;消耗性能但是效果好&#xff0c;Baked烘焙光影&#xff0c;将光的照射效果作为贴图贴在静态的物体上形成一种虚假的光照…...

简单实现通过电脑操作手机

通过电脑操作手机&#xff0c;支持单击&#xff0c;拖抓事件&#xff0c;延时有1-2秒。 具体步骤&#xff1a; 1、从手机截图到sdcard 2、将图片导出到PC 3、从PC加载展示图片 4、开启定时器 5、设置点击、滚动事件 1、 private static void takeScreenshot(String path)…...

基于ESP32的便携式游戏机

基于ESP32的便携式游戏机 一、项目说明二、项目材料三、程序测试四、设置LCD屏幕五、控制设置六、测试电路七、外壳制作八、结果 视频&#xff1a; ESP32 pro 一、项目说明 欢迎来到复古游戏的世界&#xff01;你是否曾经想要以便携格式重温童年的经典游戏&#xff1f;在这个…...

【LeetCode 88. 合并两个有序数组】 java实现

LeetCode 88. 合并两个有序数组 题目描述 给你两个有序整数数组 nums1 和 nums2&#xff0c;请你将 nums2 合并到 nums1 中&#xff0c;使 nums1 成为一个有序数组。 说明: 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 的大小等于 m n&#xff08;即…...

200Kg大载重多旋无人机价格高昂技术分析

200Kg大载重多旋无人机作为一种高度专业化的航空工具&#xff0c;其价格相较于普通无人机显著较高&#xff0c;这主要是由于其在技术设计和生产过程中所需的高要求所致。以下是对其价格高昂的技术分析&#xff1a; 一、高性能材料与结构设计 1. 高强度轻量化材料&#xff1a;…...

快速理解http的get和post

在网络通信中&#xff0c;HTTP 协议扮演着非常重要的角色&#xff0c;而不同的 HTTP 方法决定了客户端与服务器之间的交互方式。 这里讲一下最常用的两种方法——GET 和 POST。 一、GET 方法 GET 方法用于从服务器获取资源。 这就像去图书馆借书——你向图书馆请求一本特定的…...

Mamba学习笔记(3)—S4原理基础

文章目录 Efficiently Modeling Long Sequences with Structured State Spaces0 Abstract1 Introduction2 Background&#xff1a;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什么字体好看&#xff1f; 好看是一个比较主观的概念&#xff0c;见仁见智&#xff0c;在选用ppt字体时&#xff0c;比起关注好看&#xff0c;字体是否“合适”应该是优先级更高的需求。这里的合适&#xff0c;即PPT所选用字体的风格、呈现效果是否与PPT的主题和使用场景相…...

第6篇:无线与移动网络

目录 引言 6.1 无线网络的基础概念 6.2 无线局域网&#xff08;WLAN&#xff09;与IEEE 802.11 6.3 蓝牙与无线个域网&#xff08;WPAN&#xff09; 6.4 无线城域网&#xff08;WMAN&#xff09;与WiMax 6.5 ZigBee与智能家居 6.6 移动蜂窝网络&#xff08;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&#xff1a;从自注意力到MLP的工作机制 以下大部分内容本来自对3Blue1Brown的视频讲解的整理组织 一、Transformer的目标 为了简化问题&#xff0c;这里讨论的模型目标是接受一段文本&#xff0c;预测下一个词。这种任务对模型提出了两大要求&#xff1a;…...

《米小圈动画成语》|在趣味中学习,在快乐中掌握成语知识!

作为一名家长&#xff0c;我一直希望孩子能够在学习的过程中既感受到乐趣&#xff0c;又能获得真正的知识。成语作为中华文化的精华&#xff0c;虽然意义深远、简洁凝练&#xff0c;但对于一个小学生来说&#xff0c;学习和理解这些言简意赅的成语无疑是一个挑战。尤其是有些成…...

linux系统之jar启动脚本

编辑linux启动脚本 执行 vi run_blog 按i 进入编辑&#xff0c;复制以下代码&#xff0c;并根据当前环境修改三个参数。以下是详细完整脚本代码&#xff1a; #!/bin/bash# 配置部分 JAR_PATH"/path/to/your/app.jar" # 替换为你的 JAR 文件的实际路径 L…...

简单认识Maven 2-Maven坐标

Maven坐标 在 Maven 中&#xff0c;坐标&#xff08;Coordinates&#xff09;用于唯一标识一个项目或依赖项&#xff0c;就像在现实世界中通过经纬度来确定一个地理位置一样。Maven 坐标由三个主要部分组成&#xff1a;groupId、artifactId 和 version。 groupId&#xff08;…...

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) 方式一&#xff1a;bind绑定(2) 方式二&#xff1a;使用 ES6 class fields 语法(3) 方式三&#xff1a;直接传入一个箭头函数(重要) 3.事件参数传递(1) 传递事件对象event(2) 传递其他参数 4. 事件绑定…...

前端开发攻略---取消已经发出但是还未响应的网络请求

目录 注意&#xff1a; 1、Axios实现 2、Fetch实现 3、XHR实现 注意&#xff1a; 当请求被取消时&#xff0c;只会本地停止处理此次请求&#xff0c;服务器仍然可能已经接收到了并处理了该请求。开发时应当及时和后端进行友好沟通。 1、Axios实现 <!DOCTYPE html> &…...

韩信走马分油c++

韩信走马分油c 题目算法代码 题目 把油桶里还剩下的10斤油平分&#xff0c;只有一个能装3斤的油葫芦和一个能装7斤的瓦罐。如何分。 算法 油壶编号0&#xff0c;1&#xff0c;2。不同倒法有&#xff1a;把油从0倒进0&#xff08;本壶到本壶&#xff0c;无效&#xff09;&…...

【Linux】Anaconda下载安装配置Pytorch安装配置(保姆级)

目录 Anaconda下载 Anaconda安装 conda init conda --v Conda 配置 conda 环境创建 conda info --envs conda list Pytorch安装配置 检验安装情况 检验是否可以使用GPU Anaconda下载 可以通过两种途径完成Anaconda安装包的下载 途径一&#xff1a;本地windows下…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...