第三方接口设计注意要点
实际工作中,我们会遇到与三方系统对接的情形,比如对接短信服务、支付服务、地图服务、以及一些外部业务系统的调用和回调等等,不论是我们调用第三方接口还是我们为其他系统提供接口服务,调用过程中会遇到一些大大小小的问题和吐槽的地方,比如接口不通对方可能有白名单限制、接口返回字段与文档不一致、接口不稳定等等,本文总结了一些在设计第三方接口过程中要考虑的到的点。
一、接口设计
1.URL和请求方式
- 请求方式可以使用GET和POST,不推荐使用PUT和DELETE,由于其复杂性和兼容性问题,在实际开发中少用。
- 一个对外提供的接口,随着业务的变化,很大的概率会发生更新,一般来说增加字段不会影响原有接口调用,但当进行一些大业务逻辑变动或者字段必须要修改时,就需要使用新的url来处理新的业务逻辑,这个时候我们可以将接口的版本号放入URL。如:https://xxx.xxx.com/v1/products获取v1版本下的所有产品,版本号可以是整数型和浮点型。
2.请求参数
- 明确请求参数的类型、必填项和默认值,便于调用者理解和使用
- 一般用于认证的参数的公共参数放在header,其余的一般在请求body体和url参数中
- 参数校验,如果外部用户传入了一些异常值,有可能我们服务会报错,参数校验是必要的,比如邮箱格式、非空判断等
3.响应参数
- 统一接口的响应数据格式,一般为JSON
- 响应数据一般必包含三个属性:状态码code、执行结果消息message、响应数据data
{ code: 200, data: { ... }, message: '成功' }
- 列表类接口若返回数据条数很多,应提供分页返回以及其他条件筛选参数
- 异常统一处理,避免把服务内部的异常栈直接返回,当出现异常时通过code和message来统一处理,这样做好处是便于第三方了解问题原因,避免系统敏感信息(比如数据库表、代码类等)泄露。如:
{ "code":500, "message":"服务器内部错误", "data":null }
二、安全性
设计一个接口,我们往往习惯优先考虑功能性和可用性,接口安全容易被忽略,而安全性从来都是外部接口设计必不可少的一部分,如果被攻击者利用,遭受攻击和财务损失,所以我们要避免此类安全问题产生。常用的安全性措施有:
1.使用HTTPS协议
- 防止信息泄露,在数据网络传输过程中,可能会经过多个网络节点,使用http协议,数据是以明文方式传输的,这些明文中如果包含了一些敏感信息,则很容易会被攻击者截获,而HTTPS通过TLS/SSL加密算法对数据进行加密,及时攻击者截获了数据,这些数据也是加密后的数据。
- 数据涉及到一些用户的隐私信息或者财务数据时,如个人身份证号、医疗数据、银行转账信息、收款账号等,使用HTTPS加密后传输,能有效防止隐私数据被窃取。
2.对敏感数据加密传输
HTTPS加密主要是保证在网络传输过程中不会被中途拦截或窥探,但某些网络攻击场景下,攻击者可以在客户端伪造服务器的证书,并与客户端建立一个看似合法的HTTPS连接,进行中间人攻击,导致数据被人有可能被截取。所以我们通常会在应用层对数据进行额外加密作为一种补充防护措施。常见的接口加解密算法有:
- 1)对称加密,对称加密是指加密和解密使用相同的密钥,常用的对称加密算法如AES、DES,Java中的加密标准JCE提供了丰富的加密算法
- 2)非对称加密,非对称加密使用一对密钥,即公钥和私钥。公钥用于加密,私钥用于解密。常见的非对称加密算法如 RSA、ECC
- 3)消息摘要算法,消息摘要算法可以将任意长度的数据转换为固定长度的哈希值。常见的消息摘要算法有 MD5(安全性较低,已不推荐用于安全敏感场景)、SHA - 1(也逐渐被认为安全性不足)和 SHA - 256 等。哈希值可以用于验证数据在传输过程中是否被篡改。
接口加密实际应用过程中还要考虑密钥的管理和安全存储,以及加解密过程中可能出现的异常等,做好访问权限控制以及异常处理机制,数据加解密因为加密算法的复杂度和密钥长度等如果调用很频繁,还要根据实际需求考虑系统的性能表现,选择合适的加密算法以达到安全和性能的平衡。
三、加签验签
虽然数据加密可以保护数据内容不被未授权的访问者读取,但无法完全保证数据在传输过程中没有被篡改。除了恶意篡改,数据在传输过程中还可能因为网络故障、存储设备故障等原因出现错误。加签验签过程中的数字签名验证可以作为一种检查机制,及时发现这些错误。例如,在金融系统的接口数据传输中,交易金额、交易账户等关键信息的完整性至关重要,加签验签可以确保这些信息在传输过程中保持完整和准确。
加签(数字签名生成)
- 定义:加签是要指在数据发送端(客户端),使用特定的hash算法和发送方的私钥对数据进行加密,生成一个数字签名的过程。这个数字签名就像是发送方给数据加的“电子印章”,通常数字签名和报文原文一起发送给接收端。
- 具体操作
○ 哈希运算,首先要对发送的数据进行哈希运算。使用哈希算法(如 SHA - 256)会将任意长度的数据转换为固定长度的哈希值。例如,对于一段包含交易信息的文本数据,经过哈希运算后会得到一个唯一的哈希值。这个哈希值的特点是:只要数据稍有变化,哈希值就会完全不同。
○ 私钥加密哈希值:然后使用发送方的私钥对哈希值进行加密,加密后的结果就是数字签名sign。私钥是发送方拥有的密钥,这就保证了签名的唯一性和发送方身份的关联性。例如在一个电子合同签署系统中,发送方(合同签署方)使用自己的私钥对合同数据的哈希值进行加密生成数字签名,这个签名就代表了签署方对合同内容的认可。
验签(数字验签名验证)
- 定义:验签是在数据接收方,使用发送方提供的公钥和相应的算法对数字签名进行验证,以确定数据的完整性和数据来源的真实性。
- 具体操作:
○ 获取公钥,公钥一般发送方来提供
○ 解密签名并比对哈希值:接收方拿到原始报文和数字签名后,使用获取到的公钥对数字签名进行解密,得到发送方生成的原始哈希值。同时接收方对收到的数据进行与发送方相同的哈希运算,得到一个新的哈希值。然后将这两个hash值进行比较。如果两个哈希值相同,说明数据在传输过程没有被篡改,如果不同,则说明数据可能被篡改或者来源不可信。
防止重放攻击
- 什么是重放攻击:重放攻击是一种网络安全威胁,攻击者截获并保存合法的网络通信数据(如接口请求),然后在之后的某个时间重新发送这些数据,以达到欺骗接收方系统、获取非法利益或干扰系统正常运行的目的。例如,在金融交易接口中,攻击者截获了一个转账请求,然后重复发送这个请求,就可能导致资金被多次非法转移。
- 防止重放攻击的方法:使用Nonce(随机数)+Timestamp(时间戳)
- 使用时间戳(timestamp),将当前的时间戳添加到数据中一起进行加签。接收方在验签时,首先检查时间戳是否在合理的范围内(如10s)。如果服务端收到请求后判断时间戳与当前时间相比较超过了60s,则认为是非法请求,就拒绝该请求。
- 使用Nonce序列号,发送方为每个请求分配一个唯一的序列号(随机字符串),要求每次请求时,该参数要保证不同,发送方将序列号与数据一起加签发送。接收方维护一个已处理序列号的列表或者数据库记录,当收到带有序列号的请求时,检查该序列号是否已经被处理过。如果已处理,则认为是重放攻击并拒绝。
四、IP白名单
通过限制只有在白名单中的IP地址才能够访问接口,可以有效阻止来自外部恶意IP的非法请求,当攻击者尝试攻击接口(如SQL注入、暴力破解)时,如果我们设置了IP白名单,这些来之未知和恶意的IP攻击请求在网络层面就会被直接拒绝,能有效提搞接口安全性。
- 通过限制访问IP,可以防止接口被滥用,避免一些非预期来源的请求导致系统资源被过度消耗,影响其他服务
- 设置IP白名单的方式有很多,比如防火墙、网关、Spring Cloud Alibaba提供的组件Sentinel也支持白名单设置,还可以使用Java中自定义过滤器和拦截器来对请求的IP地址进行拦截。
五、幂等性
幂等性也就是我要保证我任意多次的调用产生的结果应与一次执行的结果相同,也就是落库的操作只能执行一次,也就是说接口要能够识别误操作或恶意操作调用多次接口的情况。比如订单系统,不能多次调用就产生多个订单,那就会造成数据重复错误。
常见的解决方案:
- token机制,客户端在首次请求API时,服务器生成一个唯一的token返回客户端,客户端在后续对同一操作的重复请求时,必须携带这个token。服务器端维护一个已处理token的集合或数据库记录,当收到带有token的请求时,检查Token是否已经被处理过。如果已处理,则直接返回之前的结果,不再执行重复的操作
- 数据库唯一索引,当插入数据时,如果违反唯一索引约束,数据库会抛异常来保证相同的数据不会被重复插入。
- 锁机制,悲观锁,获取数据的时候枷锁,乐观锁,基于版本号机制,在更新数据时进行校验数据,还有分布式锁如redisson等
- 状态机-状态变更,在状态机中,每个操作只能使状态按照特定的规则进行转换,当收到相同的操作请求时,根据当前的状态判断是否允许操作,如果操作不符合当前状态的转换规则,就不执行操作,从而保证幂等性。
六、可扩展性
接口设计应考虑未来的扩展需求,便于在增加新功能或调整现有的功能。
- 预留扩展字段,在接口的数据结构中可以预留一些未定义的扩展字段,比如在接口的返回JSON数据结构中,添加ext字段,用于存放未来可能添加的新数据
- 通过url版本号控制,通过版本号来管理接口的变化
- 业务代码中提高未来变化的兼容性,通过使用工厂或策略模式,能将变化的部分进行封装,让接口扩展更加灵活,减少对现有接口代用代码的影响。
七、日志记录
在第三方系统调用我们API接口时,添加日志可以帮助我们快速分析和定位问题,我们在必要的地方要添加可供排查的线索,为后期联调和维护提供排查的线索。
- API请求日志,可以把请求的信息:url、请求头、请求方式、请求时间,响应数据、响应时间等记录到日志文件。记录请求体中的参数时,需要进行适当的加密或脱敏处理。
- traceId,我们的业务系统在调用多个服务时提供整个链路的线索。
- 日志类级别,可以根据需求来决定是否开启不同日志级别和设置不同的日志类型。
八、接口文档
接口文档就像一份详细的使用说明书,能够让第三方调用者快速了解接口的功能、参数、返回值等信息,从而高效的进行开发。当接口需要进行更改和修改时,一个好的接口文档就显得很重要,通过更新文档来告知第三方开发者接口的变化情况,如新增了某个功能、修改了参数类型等。
接口文档的格式:
- 目录
- 接口详细信息
- 接口访问路径和接口名称
- 请求方法
- 请求参数,包括参数名称、类型、是否必填、默认值、参数的含义和取值范围,可以使用表格形式展示。
- 请求头信息,如果有请求头header参数,要说明请求头的名称、类型、用途、是否必填
- 请求体示例
- 响应状态码
- 响应数据格式
- 错误响应示例
- 安全说明,如身份验证方式、访问频率限制、白名单等
- 使用示例和代码片段
- 更新历史记录,记录接口文档和接口本身的更新历史,包括每次更新的版本号、更新日期、更新内容摘要等信息。