我们在项目开发的时候难免会与第三方对接,而与第三方直接的接口通讯大多数为http方式的通讯方式,那么这个时候就有个问题,两个系统直接的接口数据怎么进行校验呢,如何防止拿到接口的人通过技术手段调用呢?例如使用postman等的方式调用。
那么这篇文章就会以两种角色的角度带大家走入接口之间的安全校验机制------API提供方(服务端)和API调用方(客户端) ,分别探讨如何设计安全的接口交互方案。 那么本文做的示例就是使用 AK(api id)/SK(secret key) 签名认证API 接口。
首先我们要定义一套参数加密方案,加密方案有很多,例如
算法类型 | 安全性 | 计算速度 | 输出长度 | 适用场景 |
---|---|---|---|---|
MD5 | 低 | 最快 | 128bit | 不推荐新系统使用 |
SHA1 | 中 | 快 | 160bit | 旧系统兼容 |
SHA256 | 高 | 中 | 256bit | 推荐默认选择 |
SHA3 | 极高 | 较慢 | 可变 | 高安全要求 |
HMAC | 高 | 中 | 可变 | 带密钥签名 |
RSA | 极高 | 慢 | 可变 | 非对称场景 |
ECDSA | 极高 | 较慢 | 256/512bit | 区块链等 |
那么作为我们这篇文章的嘉宾,我们邀请到了SHA1选手
作为API提供方
作为API提供方我们需要考虑的事是我作为供应商会有很多的客户,而每个客户肯定要有一个专门的标识,那么我们就可以通过 AK(api id) 去区分每一个客户。而我们也会提供每一个AK对应的密码,即 SK(secret key) ,而这个两个内容我们肯定是需要我们作为提供方去存储起来的,而我们的客户(即调用方)我们也会提供一个AK和SK给他们。
我们需要定义一套规则,让客户需要把所有的参数组成一个字符串然后在进行sha1签名,然后我们拿到业务参数后我们也进行sha1签名,如果最后出来的字符串内容是一样的化,则说明验证通过,我们继续拿到他的业务参数进行下一步的操作。
而我们与客户的交互参数中有三个参数是最重要的,如AK(api id) 、SK(secret key) 、时间戳
- AK → "你是谁"(认证)
- SK → "你真的是你"(防伪)
- 时间戳 → "你的请求是否新鲜"(防重放)
而有了这个三个参数加上客户的业务参数就组成了一个字符串然后再进行sha1签名,就得到了一个加密过后的sha1字符串,然后在把这个sign(签名串)添加到参数中请求我们,我们就可以进行接口校验,从而达到系统交互之间的安全交互。

作为调用方
上面我们讲了作为服务提供方提供的方案,那么我们如果要想签名认证通过的话我们是需要使用和提供方一样的签名方案的。而这个签名方案一般都是提供方提供的,我们只需要根据他的对接文档进行对接即可
首先我们要确认的提供方的一个加密方案,例如上面提供的方案:
参数1=值1&参数2=值2&ak=xxx&时间戳=xxx
再把上面的值进行sha1 加密,即可生成一个sign:
java
private String sha1(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] result = digest.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : result) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-1 algorithm not found", e);
}
}
例如提供方的接口需要的业务参数是这样的:
json
{
"参数1": 1234,
"参数2": "dr576f8",
"参数3": "fefeww"
}
而一般还需要添加三个参数:
json
{
"app_id": "342345",
"timestamp": 1739345600,
"sign": "aa442339f6535e635c2a62cd035d25bfa8771bcc"}
而 sign
就是我们上面把业务参数组装好之后经过sha1计算出来的签名, 把这个也加入到请求体中,最后的请求body为:
json
{
"参数1": 1234,
"参数2": "dr576f8",
"参数3": "fefeww",
"app_id": "342345",
"timestamp": 1739345600,
"sign": "aa442339f6535e635c2a62cd035d25bfa8771bcc"
}
其中我们的sk是加入到sign签名里面去的,所以没有直接暴露在请求体中。这样,提供方就知道了以下内容
- AK → "你是谁"(认证)
- SK → "你真的是你"(防伪)
- 时间戳 → "你的请求是否新鲜"(防重放)
- 业务参数 → "具体你要干什么"(业务请求)
这样一套api的验证机制就做好了,如果我们有回调接口的话我们则会和提供方保持一样的验证方式,那么则需要提供方在回调我们的接口的时候也吧上面四个内容传过来,这样就能保证双方的api安全了。一般我们会把回调地址以业务参数的形式传递给提供方。
除此之外,因为我们都是http的方式进行调用,那么我们难免会遇见像网络错误这种情况,那么我们应该也需要在双方都添加重试机制,如果你是java客户端,可以看看作者之前写过的文章:
如果你需要使用重试机制,请使用Spring官方的Spring Retry
那么最为最后的结尾,这里只是拿sha1举例,其实大家可以换成其他的签名算法也是没有问题的。当调用方和提供方都能遵循这套"数字礼仪",三方系统间的对话才能真正做到既安全又流畅。
如果非要弄简单的不需要鉴权的api接口,则可以考虑添加白名单机制,即把调用方的ip地址加入白名单,拒绝掉非此ip的请求。当然选哪个方法还是需要结合本身系统的特性选择合适的方案。
