在为第三方系统提供接口的时候,肯定要考虑接口数据的安全问题,比如数据是否被篡改,数据是否已经过时,数据是否可以重复提交等问题
在设计三方接口调用的方案时,需要考虑到安全性和可用性。以下是一种设计方案的概述,其中包括使用API密钥(Access Key/Secret Key)进行身份验证和设置回调地址。
设计方案概述
-
API密钥生成:为每个三方应用生成唯一的API密钥对(AK/SK),其中AK用于标识应用,SK用于进行签名和加密。
AK:Access Key Id,用于标示用户。
SK:Secret Access Key,是用户用于加密认证字符串和用来验证认证字符串的密钥,其中SK必须保密。
通过使用Access Key Id / Secret Access Key加密的方法来验证某个请求的发送者身份。 -
接口鉴权:在进行接口调用时,客户端需要使用AK和请求参数生成签名,并将其放入请求头或参数中以进行身份验证。
淘宝签名和验签:淘宝SDK签名算法 (yuque.com)
-
回调地址设置:三方应用提供回调地址,用于接收异步通知和回调结果。
-
接口API设计:设计接口的URL、HTTP方法、请求参数、响应格式等细节。
权限划分
-
appID:应用的唯一标识
用来标识你的开发者账号的, 即:
用户id
, 可以在数据库添加索引,方便快速查找,同一个 appId 可以对应多个 appKey+appSecret,达到权限的 -
appKey:公匙(相当于账号)
公开的,调用服务所需要的密钥。是用户的身份认证标识,用于调用平台可用服务.,可以简单理解成是账号。
-
appSecret:私匙(相当于密码)
签名的密钥,是跟appKey配套使用的,可以简单理解成是密码。
-
token:令牌(过期失效)
使用方法
-
向第三方服务器请求授权时,带上
AppKey和AppSecret
(需存在服务器端) -
第三方服务器验证
appKey和appSecret
在数据库、缓存中有没有记录 -
如果有,生成一串唯一的字符串
(token令牌)
,返回给服务器,服务器再返回给客户端 -
后续客户端每次请求都需要带上token令牌
为什么 要有appKey + appSecret 这种成对出现的机制呢,?
-
因为
要加密
,通常用在首次验证(类似登录场景)
, 用appKey(标记要申请的权限有哪些)
+appSecret(密码, 表示你真的拥有这个权限)
来申请一个token, 就是我们经常用到的accessToken(通常拥有失效时间)
, 后续的每次请求都需要提供accessToken 表明验证权限通过。
现在有了统一的appId,此时如果针对同一个业务要划分不同的权限,比如同一功能,某些场景需要只读权限,某些场景需要读写权限。这样提供一个appId和对应的秘钥appSecret就没办法满足需求。此时就需要
根据权限进行账号分配
,通常使用appKey和appSecret。
-
由于
appKey 和 appSecret 是成对出现的账号
,同一个 appId 可以对应多个 appKey+appSecret,
这样平台就为不同的appKey+appSecret对
分配不一样的权限,-
可以生成两对appKey和appSecret。一个用于删除,一个用于读写,达到权限的细粒度划分。如 : appKey1 + appSecect1 只有删除权限 但是 appKey2+appSecret2 有读写权限… 这样你就可以把对应的权限 放给不同的开发者。其中
权限的配置都是直接跟appKey 做关联的
, appKey 也需要添加数据库索引, 方便快速查找
-
简化的场景:
-
第一种场景:通常用于开放性接口,像地图api,
会省去app_id和app_key
,此时相当于三者相等,合而为一appId = appKey = appSecret,
。这种模式下,带上app_id的目的仅仅是统计某一个用户调用接口的次数而已了。 -
第二种场景: 当每一个用户有且仅有一套权限配置 可以
去掉 appKey,
, 直接将app_id = app_key
, 每个用户分配一个appId+ appSecret
就够了`.
也可以
可以采用签名(signature)的方式:当调用方向服务提供方法发起请求时,带上(
appKey、时间戳timeStamp、随机数nonce、签名sign
) 签名sign 可以使用(AppSecret + 时间戳 + 随机数)
使用sha1、md5
生成,服务提供方收到后,生成本地签名和收到的签名比对,如果一致,校验成功
签名流程
签名规则
-
分配
appId(开发者标识)
和appSecret(密钥)
,给不同的调用方
可以直接通过平台线上申请,也可以线下直接颁发。appId是全局唯一的,每个appId将对应一个客户,密钥appSecret需要高度保密。
-
加入
timeStamp
(时间戳),以服务端当前时间为准,单位为ms ,5分钟内数据有效时间戳的目的就是为了减轻DOS攻击。防止请求被拦截后一直尝试请求接口。服务器端设置时间戳阀值,如果
服务器时间 减 请求时间戳
超过阀值,表示签名超时,接口调用失败。 -
加入临时流水号
nonce
,至少为10位 ,有效期内防重复提交
。随机值nonce 主要是为了
增加签名sign的多变性
,也可以保护接口的幂等性
,相邻的两次请求nonce不允许重复,如果重复则认为是重复提交,接口调用失败。通过在接口签名请求参数加上 时间戳timeStamp + 随机数nonce 可以防止 ”重放攻击“
1.时间戳(timeStamp):
以服务端当前时间为准,服务端要求客户端发过来的时间戳,必须是最近60秒内(假设值,自己定义)的。
这样,即使这个请求即使被截取了,也只能在60s内进行重放攻击。
2.随机数(nonce):
但是,即使设置了时间戳,攻击者还有60s的攻击时间呢!
所以我们需要在客户端请求中再加上一个随机数(中间黑客不可能自己修改随机数,因为有参数签名的校验呢),
服务端会对一分钟内请求的随机数进行检查,如果有两个相同的,基本可以判定为重放攻击。
因为正常情况下,在短时间内(比如60s)连续生成两个相同nonce的情况几乎为0服务端“第一次”在接收到这个nonce的时候做下面行为:
1.去redis中查找是否有key为nonce:{ nonce}的数据
2.如果没有,则创建这个key,把这个key失效的时间和验证timestamp失效的时间一致,比如是60s。
3.如果有,说明这个key在60s内已经被使用了,那么这个请求就可以判断为重放请求。-
针对查询接口,流水号只用于日志落地,便于后期日志核查。
-
针对办理类接口需校验流水号在有效期内的唯一性,以避免重复请求。
-
-
加入签名字段
sign
,获取调用方传递的签名信息。通过在接口签名请求参数加上 时间戳appId + sign 解决身份验证和防止 ”参数篡改“
1.请求携带参数appId和Sign,只有拥有合法的身份appId和正确的签名Sign才能放行。这样就解决了身份验证和参数篡改问题。
2.即使请求参数被劫持,由于获取不到appSecret(仅作本地加密使用,不参与网络传输),也无法伪造合法的请求。
以上字段放在请求头中。
API接口设计
根据你的具体需求和业务场景,以下是一个简单示例的API接口设计:
1. 获取资源列表接口
-
URL: /api/resources
-
HTTP 方法: GET
-
请求参数:
-
page (可选): 页码
-
limit (可选): 每页限制数量
-
-
响应:
-
成功状态码: 200 OK
-
响应体: 返回资源列表的JSON数组
-
2. 创建资源接口
-
URL: /api/resources
-
HTTP 方法: POST
-
请求参数:
-
name (必填): 资源名称
-
description (可选): 资源描述
-
-
响应:
-
成功状态码: 201 Created
-
响应体: 返回新创建资源的ID等信息
-
3. 更新资源接口
-
URL: /api/resources/{resourceId}
-
HTTP 方法: PUT
-
请求参数:
-
resourceId (路径参数, 必填): 资源ID
-
name (可选): 更新后的资源名称
-
description (可选): 更新后的资源描述
-
-
响应:
-
成功状态码: 200 OK
-
4. 删除资源接口
-
URL: /api/resources/{resourceId}
-
HTTP 方法: DELETE
-
请求参数:
-
resourceId (路径参数, 必填): 资源ID
-
-
响应:
-
成功状态码: 204 No Content
-