瞧瞧别人家的API接口,那叫一个优雅

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

在实际工作中我们需要经常跟第三方平台打交道可能会对接第三方平台API接口或者提供API接口给第三方平台调用。

那么问题来了如果设计一个优雅的API接口能够满足安全性、可重复调用、稳定性、好定位问题等多方面需求

今天跟大家一起聊聊设计API接口时需要注意的一些地方希望对你会有所帮助。

1. 签名

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

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

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

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

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

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

问题来了签名中为什么要加时间戳

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

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

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

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

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

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次。

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

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

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

5. 参数校验

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

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

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

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

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

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

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

在Java中校验数据使用最多的是hiberateValidator框架它里面包含了@Null、@NotEmpty、@Size、@Max、@Min等注解。

用它们校验数据非常方便。

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

6. 统一返回值

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

{
    "code":0,
    "message":null,
    "data":[{"id":123,"name":"abc"}]
},

签名错误返回的json格式

{
    "code":1001,
    "message":"签名错误",
    "data":null
}

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

{
    "rt":10,
    "errorMgt":"没有权限",
    "result":null
}

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

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

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

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

业务系统在出现异常时抛出业务异常的RuntimeException其中有个message字段定义异常信息。

所有的API接口都必须经过API网关API网关捕获该业务异常然后转换成统一的异常结构返回这样能统一返回值结构。

7. 统一封装异常

我们的API接口需要对异常进行统一处理。

不知道你有没有遇到过这种场景有时候在API接口中需要访问数据库但表不存在或者sql语句异常就会直接把sql信息在API接口中直接返回。

返回值中包含了异常堆栈信息数据库信息错误代码和行数等信息。

如果直接把这些内容暴露给第三方平台是很危险的事情。

有些不法分子利用接口返回值中的这些信息有可能会进行sql注入或者直接脱库而对我们系统造成一定的损失。

因此非常有必要对API接口中的异常做统一处理把异常转换成这样

{
    "code":500,
    "message":"服务器内部错误",
    "data":null
}

返回码code500返回信息message服务器内部异常

这样第三方平台就知道是API接口出现了内部问题但不知道具体原因他们可以找我们排查问题。

我们可以在内部的日志文件中把堆栈信息、数据库信息、错误代码行数等信息打印出来。

我们可以在gateway中对异常进行拦截做统一封装然后给第三方平台的是处理后没有敏感信息的错误信息。

8. 请求日志

在第三方平台请求你的API接口时接口的请求日志非常重要通过它可以快速的分析和定位问题。

我们需要把API接口的请求url、请求参数、请求头、请求方式、响应数据和响应时间等记录到日志文件中。

最好有traceId可以通过它串联整个请求的日志过滤多余的日志。

当然有些时候请求日志不光是你们公司开发人员需要查看第三方平台的用户也需要能查看接口的请求日志。

这时就需要把日志落地到数据库比如mongodb或者elastic search然后做一个UI页面给第三方平台的用户开通查看权限。这样他们就能在外网查看请求日志了他们自己也能定位一部分问题。

9. 幂等设计

第三方平台极有可能在极短的时间内请求我们接口多次比如在1秒内请求两次。有可能是他们业务系统有bug或者在做接口调用失败重试因此我们的API接口需要做幂等设计

也就是说要支持在极短的时间内第三方平台用相同的参数请求API接口多次第一次请求数据库会新增数据但第二次请求以后就不会新增数据但也会返回成功。

这样做的目的是不会产生错误数据。

我们在日常工作中可以通过在数据库中增加唯一索引或者在redis保存requestId和请求参来保证接口幂等性。

10. 限制记录条数

对于对我提供的批量接口一定要限制请求的记录条数

如果请求的数据太多很容易造成API接口超时等问题让API接口变得不稳定。

通常情况下建议一次请求中的参数最多支持传入500条记录。

如果用户传入多余500条记录则接口直接给出提示。

建议这个参数做成可配置的并且要事先跟第三方平台协商好避免上线后产生不必要的问题。

11. 压测

上线前我们务必要对API接口做一下压力测试知道各个接口的qps情况。

以便于我们能够更好的预估需要部署多少服务器节点对于API接口的稳定性至关重要。

之前虽说对API接口做了限流但是实际上API接口是否能够达到限制的阀值这是一个问号如果不做压力测试是有很大风险的。

比如你API接口限流1秒只允许50次请求但实际API接口只能处理30次请求这样你的API接口也会处理不过来。

我们在工作中可以用jmeter或者apache benc对API接口做压力测试。

12. 异步处理

一般的API接口的逻辑都是同步处理的请求完之后立刻返回结果。

但有时候我们的API接口里面的业务逻辑非常复杂特别是有些批量接口如果同步处理业务耗时会非常长。

这种情况下为了提升API接口的性能我们可以改成异步处理

在API接口中可以发送一条mq消息然后直接返回成功。之后有个专门的mq消费者去异步消费该消息做业务逻辑处理。

直接异步处理的接口第三方平台有两种方式获取到。

第一种方式是我们回调第三方平台的接口告知他们API接口的处理结果很多支付接口就是这么玩的。

第二种方式是第三方平台通过轮询调用我们另外一个查询状态的API接口每隔一段时间查询一次状态传入的参数是之前的那个API接口中的id集合。

13. 数据脱敏

有时候第三方平台调用我们API接口时获取的数据中有一部分是敏感数据比如用户手机号、银行卡号等等。

这样信息如果通过API接口直接保留到外网是非常不安全的很容易造成用户隐私数据泄露的问题。

这就需要对部分数据做数据脱敏了。

我们可以在返回的数据中部分内容用星号代替。

已用户手机号为例182****887

这样即使数据被泄露了也只泄露了一部分不法分子拿到这份数据也没啥用。

14. 完整的接口文档

说实话一份完整的API接口文档在双方做接口对接时可以减少很多沟通成本让对方少走很多弯路。

接口文档中需要包含如下信息

  1. 接口地址

  2. 请求方式比如post或get

  3. 请求参数和字段介绍

  4. 返回值和字段介绍

  5. 返回码和错误信息

  6. 加密或签名示例

  7. 完整的请求demo

  8. 额外的说明比如开通ip白名单。

接口文档中最好能够统一接口和字段名称的命名风格比如都用驼峰标识命名。

接口地址中可以加一个版本号v1比如v1/query/getCategory这样以后接口有很大的变动可以非常方便升级版本。

统一字段的类型和长度比如id字段用Long类型长度规定20。status字段用int类型长度固定2等。

统一时间格式字段比如time用String类型格式为yyyy-MM-dd HH:mm:ss。

接口文档中写明AK/SK和域名找某某单独提供等。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6