API签名认证的说明及实现

这篇具有很好参考价值的文章主要介绍了API签名认证的说明及实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

请思考一个问题

请思考一个重要的问题:如果我们为开发者提供了一个接口,却对调用者一无所知。假设我们的服务器只能允许100个人同时调用接口。如果有攻击者疯狂的请求这个接口,那将极其危险。一方面这可能会损害我们的安全性,另一方面也可能耗尽服务器性能,影响正常用户的使用。

因此,我们必须为接口设置保护措施。例如限制每个用户每秒只能调用10次接口,即实施请求频次的限额控制。

现在,我们设计一个方法,来确定谁在调用调用 。在开发后端时,我们会进行一些权限检查。比如:当管理员执行删除操作时,后端需要检查这个用户是否为管理员。那么,是如何获取用户信息的呢?是否直接从后端的session中获取?但问题来了,当前端调用接口的时候,一定有session吗?比如说,是前端直接发起请求,并没有登录操作,没有输入用户名和密码,那怎么去调用呢?因此,一般情况下,会采用一个叫 API签名认证 的机制。

什么是API签名认证

简单的说,如果你想来我家做客,我不可能随便让任何陌生人进来。所以我会提前给你发一个类似请帖的东西,作为授权或许可证。当你来访问我的时候,你需要带上这个许可证。我可能并不认识你,但我认识你的请帖。只要你有这个请帖,我就允许你进来。

所以,API签名认证主要包括两个过程:一是签发签名,二是使用签名或校验签名。

为什么需要API签名认证

  1. 保证安全性,不能让任何人都能调用。
  2. 适用于无需保存登录态的场景。只认签名,不关注用户登录态。

如何在后端实现签名认证

需要两个东西:accessKey 和 secretKey
这和用户名和密码类似,不过每次调用接口都需要带上,实现 无状态的请求 。这样,即使你之前没有来过,只要这次的状态正确,你就可以调用接口。所以需要这两个东西来标识用户。

签名认证实现

在签发的过程中,可以自己编写一个生成 accessKey 和 secretKey 的工具。一般来说,accessKey 和 secretKey 需要尽可能复杂,以防止黑客尝试破解,特别是密码,需要尽可能复杂,无规律。

通过 http request header 头传递参数

  • 参数1:accessKey :调用的标识 userA、userB(复杂、无序、无规律)
  • 参数2:secretKey:秘钥(复杂、无序、无规律)该参数不能放到请求头中
  • 参数3:用户请求参数
  • 参数4:sign

⚠️注意:千万不能把秘钥直接在服务器之间传递,有可能会被拦截。

加密方式

用户参数 + 秘钥 => 签名生成算法(SHA-256、SHA-3等) => 不可解密的值
比如:abc + abdedfgh => sfdasidfhssdfh

怎么知道这个签名对不对?

服务端用一模一样的参数和算法去生成签名,只要和用户传的一致,就表示一致。

怎么防重放?

  • 参数5:加 nonce 随机数,只能用一次(服务端要保存用过的随机数)

    每次请求时,发生一个随机数给后端。后端只接受并认可该随机数一次,一旦随机数被使用过,后端将不再接受相同的随机数。这种方式解决了请求重复的问题,因为即使对方使用之前的时间和随机数进行请求,后端会认识到该请求已经被处理过,不会再次处理。

    但是,这种方法需要后端额外开发来保存已使用的随机数。并且,如果接口的并发量很大,每次请求都需要一个随机数,那么可能会面临处理百万、千万甚至上亿级别请求的情况。因此,除了使用随机数之外,还需要其他机制来定期清理已使用的随机数。

  • 参数6:加 timestamp 时间戳,检验时间戳是否过期。

    每个请求在发生时携带一个时间戳,并且后端会验证该时间戳是否在指定的时间范围内。例如不超过10分钟或5分钟。这可以防止对方使用昨天的请求在今天进行重放。

    通过这种方式,我们可以一定程度上控制随机数的过期时间。因为后端需要同时验证这两个参数,只要时间戳过期 或者 随机数被使用过,后端会拒绝该请求。因此,时间戳可以在一定程度上减轻后端保存随机数的负担。通常情况下,这两张方法可以相互配和使用。

因此,在标准的签名认证算法中,建议至少添加以下五个参数:accessKey、secretKey、sign、nonce、timestamp。此外,建议将用户请求的其他参数,例如接口中的name参数,也添加到签名中,以增加安全性。

API签名认证是一个很灵活的设计,具体要有哪些参数、参数名 一定要根据场景来。(比如:userId、appId、version、固定值等)

📢 类似于HTTPS协议,签名认证的本质是确保密码不在服务器之间传输。因为任何在服务器之间传输的内容都有可能被拦截。所以,请记住密码绝不能在服务器之间传输。

Go 代码实现

源码地址: GitHub-golang版本(有对应的单元测试代码)文章来源地址https://www.toymoban.com/news/detail-685503.html

sign.go

package utils

import (
	"crypto/md5"
	"encoding/hex"
)

// 计算API签名
func CalculateSignature(accessKey, secretKey, nonce, timestamp, requestBody string) string {
	// 将参数拼接成一个字符串
	concatenatedString := accessKey + nonce + timestamp + requestBody + secretKey

	// 计算 MD5 值
	signature := md5.Sum([]byte(concatenatedString))
	return hex.EncodeToString(signature[:])
}

service.go

package main

import (
	"net/http"
	"simple/api-signature/service/utils"

	"github.com/gin-gonic/gin"
)

var AccessKey, SecretKey string = "aaa", "123456"

func GetNameByGet(c *gin.Context) {
	name := c.Query("name")

	headers := c.Request.Header
	accessKey := headers.Get("accessKey")
	nonce := headers.Get("nonce")
	timestamp := headers.Get("timestamp")
	sign := headers.Get("sign")

	if accessKey != AccessKey {
		c.JSON(http.StatusForbidden, gin.H{"error": "用户不存在"})
		return
	}

	// 计算签名
	signature := utils.CalculateSignature(accessKey, SecretKey, nonce, timestamp, "")

	// 验证签名
	if signature != sign {
		c.JSON(http.StatusForbidden, gin.H{"error": "签名验证失败"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"data": "GET 你的名字是" + name})
}

func main() {
	r := gin.New()
	r.GET("/api/name", GetNameByGet)
	r.Run(":8011")
}

client.go

package client

import (
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"simple/api-signature/client/utils"
	"strconv"
	"time"
)

/** 生成包含N个随机数字的字符串
 */
func GenetateRandomString(length int) string {
	// 设置随机数种子,以确保每次运行生成的随机数都不同
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	// 定义一个包含数字字符的字符集
	charset := "0123456789"
	charsetLength := len(charset)

	// 生成随机数字并拼接字符串
	randomString := make([]byte, length)
	for i := 0; i < length; i++ {
		randomIndex := r.Intn(charsetLength)
		randomChar := charset[randomIndex]
		randomString[i] = randomChar
	}
	return string(randomString)
}

// 获得请求头
func GetRequestHeaders(accessKey, secretkey, requestBody string) http.Header {
	headers := make(http.Header)

	// 生成 nonce : 一个包含100个随机数字的字符串
	nonce := GenetateRandomString(100)

	// 当前时间戳(秒级别)
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)

	// 计算签名
	signature := utils.CalculateSignature(accessKey, secretkey, nonce, timestamp, requestBody)

	// 设置请求头
	headers.Set("accessKey", accessKey)
	headers.Set("nonce", nonce)
	headers.Set("timestamp", timestamp)
	headers.Set("sign", signature)

	return headers
}

func SendApi(name, accessKey, secretKey string) (statusCode int, contentType string, bodyBytes []byte, err error) {
	requestURL := "http://localhost:8011/api/name"

	// 构建查询字符串,将其附加到URL上
	params := url.Values{}
	params.Set("name", name)

	// 构建包含查询参数的URL
	fullURL := fmt.Sprintf("%s?%s", requestURL, params.Encode())

	client := &http.Client{}

	req, err := http.NewRequest("GET", fullURL, nil)
	if err != nil {
		fmt.Println("Failed to create request, err=", err)
		return
	}

	// 构建请求头
	headers := GetRequestHeaders(accessKey, secretKey, "")
	req.Header = headers

	response, err := client.Do(req)
	if err != nil {
		fmt.Println("Failed to make request, err=", err)
		return
	}
	defer response.Body.Close()

	// 读取响应体,将响应体内容原封不动地返回给前端
	bodyBytes, err = io.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Failed to read response, err=", err)
		return
	}

	statusCode = response.StatusCode
	contentType = response.Header.Get("Content-Type")

	return
}

到了这里,关于API签名认证的说明及实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java编程实现电子签名

    本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 今天,项目甲方问我要一个电子签名;公司美工又休假。没办法,只好自己做一个啰。 尝试了几个地方工具,最好用最方便的是520transform;链接如下: http://www.520transform.cn/ 如果不使用第三方工具,需要自己开

    2024年02月03日
    浏览(21)
  • springboot整合https使用自签名证书实现浏览器和服务器之间的双向认证

    效果描述: 本地环境  两台以上电脑  可以实现安装客户端证书的电脑可以访问springboot启动项目,没有安装客户端证书的电脑无法访问springboot启动项目 1.操作:需要安装openssl工具 工具包:Win64OpenSSL_Light-3_3_0.exe 或者Win64OpenSSL_Light-3_3_0.msi  官网:[ Downloads ] - /source/index.html

    2024年04月28日
    浏览(37)
  • 三方开放接口,Springboot通过AOP实现API接口的签名验证

    前言 对外开放的接口,需要验证请求方发送过来的数据确实是由发送方发起的,并且中途不能被篡改和伪造,所以才会对接口的访问进行签名验证,以保证双方获取到的原来的信息是没有经过篡改的。 实现方法 对请求的信息内容,通过MD5运算或者其他算法(必须是 不可逆的

    2024年02月07日
    浏览(41)
  • 网络防御 --- 认证与各项技术

    数据认证是指保证数据的真实性、完整性和可信度,以确保数据不被篡改或伪造。其作用包括但不限于: 保护关键数据不被恶意篡改或损坏 提供数据来源的可靠性和安全性,使其更容易被公众所信任 将数字签名应用到数据中,以便证明数据已被验证且未被篡改 常见的实现技

    2023年04月22日
    浏览(34)
  • 对接第三方接口鉴权(Spring Boot+Aop+注解实现Api接口签名验证)

    一个web系统,从接口的使用范围也可以分为对内和对外两种,对内的接口主要限于一些我们内部系统的调用,多是通过内网进行调用,往往不用考虑太复杂的鉴权操作。但是,对于对外的接口,我们就不得不重视这个问题,外部接口没有做鉴权的操作就直接发布到互联网,而

    2024年04月29日
    浏览(65)
  • 【AGC】认证服务HarmonyOS(api9)实现手机号码认证登录

    【问题背景】 近期AGC上线了HarmonyOS(api9)平台的SDK,这样api9的设备也能使用认证服务进行快速认证登录了。下面为大家带来如何使用auth SDK(api9)实现手机号码认证登录。 【开通服务】 1.登录AppGallery Connect,点击“我的项目”,在项目的应用列表中选择您需要开通认证服务的

    2024年02月15日
    浏览(36)
  • 【Sa-Token】SpringBoot 整合 Sa-Token 快速实现 API 接口签名安全校验

    在涉及跨系统接口调用时,我们容易碰到以下安全问题: 请求身份被伪造 请求参数被篡改 请求被抓包,然后重放攻击 sa-token api-sign 模块将帮你轻松解决以上难题。(此插件是内嵌到 sa-token-core 核心包中的模块,开发者无需再次引入其它依赖,插件直接可用) 假设我们有如

    2024年02月17日
    浏览(34)
  • RSA数字签名认证

    1.数字签名 数字签名是一种用于验证数据完整性和身份认证的加密技术。使用数字签名可以提高数据传输的安全性和可靠性,确保数据在传输过程中不被篡改或伪造,并且可以确定数据的发送者身份。 数字签名可以确保数据的完整性和安全性。 2.步骤 发送方使用 私钥 对要发

    2024年02月04日
    浏览(31)
  • 【鸿蒙手机】获取UDID,并添加签名认证

    一、打开开发者模式         1、手机型号华为nova 10 pro , HarmonyOS版本 4.0,路径:设置- 关于本机- 多次连续点击”软件版本“ 这一行,一般是是5到7次(我是点击了5次),第一次会弹出输入密码,验证成功后,开启开发者模式,如何查看开发模式在设置-系统和更新-开发人员

    2024年02月21日
    浏览(52)
  • 消息认证码以及数字签名的认识

    1.1 消息认证 消息认证码(message authentication)是一种确认完整性并进行认证的技术,取三个字母的首写简称为 MAC 思考针对上面这样一个场景如何去进行改进? 从哈希函数入手,将需要发送的数据进行哈希运算,将哈希值和原始值一并发送,需要在进行哈希运算的时候引入加

    2024年02月05日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包