20221226-优雅的API接口

设计api 接口时,需要注意的一些地方,希望对你会有所帮助

如何设计一个优雅的api接口,能够满足 安全性,可重复调用性,稳定性,好定位问题等多方面问题

1. 签名

为了防止api 接口中的数据被篡改,很多时候我们需要对api 接口做 签名

接口请求方将 请求参数+时间戳+密钥 拼接成一个字符串,然后通过md5 等hash 算法,生成一个签名sign

然后在请求参数或者请求头中,增加sign 参数,传递给api 接口

API 接口的网关服务,获取到该sign 值,然后用相同的请求参数+时间戳+密钥拼接成一个字符串,用相同的md5算法生成另外一个sign,对比两个sign 值是否相等

如果两个sign 相等,则认为是有效请求,api 接口的网关服务会将给请求转发给相应的业务系统

如果两个sign 不相等,则spi 接口的网关服务会直接返回签名错误

签名中为什么要加时间戳

为了安全性考虑,防止同一次请求被反复利用,增加了密钥没被破解的可能性,我们必须要对每次请求都设置一个合理的过期时间,比如15分钟

这样一次请求,在15分钟之内是有效的,超过15分钟,api 接口的网关服务会返回超过有效期的异常提示

目前生成签名中的密钥有两种形式

一种是双方约定一个固定值 privateKey

另一种是api 接口提供方给出AK/SK 两个值,双方约定用sk 作为签名中的密钥,AK接口调用方作为header 中的accessKey 传递给api 接口提供方,这样api 接口提供方可以根据AK 获取到SK,而生成新的sign

2. 加密

有些时候,我们的api 接口直接传递的非常重要的数据,比如 用户的银行卡号,用户身份证等,如果将这些参数,直接明文,暴露到公网上是非常危险的事情

目前使用比较多的是用BASE64 加密解密

我们可以将所有的数据,按照一定的规律拼接成一个大的字符串,然后在加一个密钥,拼接到一起

然后使用JDK1.8 之后的Base64 工具类处理,效果如下

【加密前的数据】www.baidu.com
【加密后的数据】d3d3LmJhaWR1LmNvbQ==

为了安全性,使用Base64 可以加密多次

API 接口的调用方在传递参数时,body 中只有一个参数data 它就是base64 之后的加密数据

API 接口的网关服务,在接收到data 数据后,根据双方事先预定的密钥、加密算法、加密次数等,进行解密,并且反序列化出参数数据

3. ip 白名单

为了进一步加强api 接口的安全性,防止接口的签名或者加密被破解了,攻击者可以在自己的服务器上请求该接口

需求限制请求ip 增加ip 白名单

只有在白名单中的ip 地址,才能成功请求api 接口,否则直接返回无访问权限

ip 白名单也可以加载api 网关服务上

但也要防止公司的内部应用服务器被攻破,这种情况也可以从内部服务器上发起api 接口的请求

这时候就需要增加web 防火墙了,比如 modsecurity 等

4. 限流

如果你的api 接口被第三方平台调用了,这就意味着,调用频率是没法控制的

第三方平台调用你的api 接口时,如果并发量一下子太高,可能会导致你的api 服务不可用,接口直接挂掉

由此,必须要对api 接口做限流

限流方法有三种

  1. 对请求ip 做限流,比如同一个ip ,在一分钟内,对 api 接口总的请求次数,不能超过10000次

对请求接口做限流 比如同一个ip 在一分钟内,对指定的api 接口,请求次数少不能超过2000次

对请求用户做限流,比如同一个AK/SK 用户,在一分钟内,对api 接口总的请求次数,不能超过10000次

我们在实际工作中,可以通过nginx redis 或者gateway实现限流的功能

5. 参数校验

我们需要对api 接口做参数校验,比如:校验必填字段是否为空,校验字段类型,校验字段长度,校验枚举值等等

这样做可以拦截一些无效的请求

比如在新增数据时,字段长度超过了数据字段的最大长度,数据库会直接报错

但这种异常的请求,我们完全可以在api 接口的前期进行识别,没有必要走到数据库保存数据那一步,浪费系统资源

有些金额字段,本来是正数,但如果用户传入了负数,万一接口没做校验,可能会导致一些没必要的损失

还有些状态字段,如果不做校验,用户如果传入了系统中不存在的枚举值,就会导致保存的数据异常

由此可见,做参数校验是非常有必要的

在java 中校验数据使用最多的是hiberate 的validator 框架,它里面包含了null notempty size max min 等注解

用它们校验数据非常方便

当然有些日期字段和枚举字段,可能需要通过自定义注解的方式实现参数校验

6. 统一返回值

我之前调用过别人的api 接口,正常返回数据是一种json 格式,比如

1
2
3
4
5
{
"code":0,
"message":null,
"data":[{"id":123,"name":"abc"}]
},

签名错误返回的json 格式

1
2
3
4
5
{
"code":1001,
"message":"签名错误",
"data":null
}

没有数据权限返回的json 格式

1
2
3
4
5
{
"rt":10,
"errorMgt":"没有权限",
"result":null
}

这是比较坑的做法,返回值中有多种不同格式的返回数据,这样会导致对接方很难理解

出现这种情况,可能是api 网关定义了一直返回值接口,业务系统定义了另外一种返回值结构,如果是网关异常,则返回网关定义的返回值结构,如果是业务系统异常,则返回业务系统的返回值结构

但这样会导致api 接口出现不同的异常,返回不同的返回值结构,非常不利于接口的维护

其实这个问题我们可以在设计 api 网关时解决

业务系统在出现异常时,抛出

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2024 TeX_baitu
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~