如何设计⼀个⽜逼的API接⼝
在⽇常开发中,总会接触到各种接⼝。前后端数据传输接⼝,第三⽅业务平台接⼝。⼀个平台的前后端数据传输接⼝⼀般都会在内⽹环境下通信,⽽且会使⽤安全框架,所以安全性可以得到很好的保护。这篇⽂章重点讨论⼀下提供给第三⽅平台的业务接⼝应当如何设计?我们应该考虑哪些问题?
主要从以上三个⽅⾯来设计⼀个安全的API接⼝。
⼀安全性问题
安全性问题是⼀个接⼝必须要保证的规范。如果接⼝保证不了安全性,那么你的接⼝相当于直接暴露在公⽹环境中任⼈蹂躏。
获取token⼀般会涉及到⼏个参数appid,appkey,timestamp,nonce,sign。我们通过以上⼏个参数来获取调⽤系统的凭证。
appid和appkey可以直接通过平台线上申请,也可以线下直接颁发。appid是全局唯⼀的,每个appid将对应⼀个客户,appkey需要⾼度保密。
timestamp是时间戳,使⽤系统当前的unix时间戳。时间戳的⽬的就是为了减轻DOS攻击。防⽌请求被拦截后⼀直尝试请求接⼝。服务器端设置时间戳阀值,如果请求时间戳和服务器时间超过阀值,则响应失败。
nonce是随机值。随机值主要是为了增加sign的多变性,也可以保护接⼝的幂等性,相邻的两次请求no
nce不允许重复,如果重复则认为是重复提交,响应失败。sign是参数签名,将appkey,timestamp,nonce拼接起来进⾏md5加密(当然使⽤其他⽅式进⾏不可逆加密也没问题)。
token,使⽤参数appid,timestamp,nonce,sign来获取token,作为系统调⽤的唯⼀凭证。token可以设置⼀次有效(这样安全性更⾼),也可以设置时效性,这⾥推荐设置时效性。如果⼀次有效的话这个接⼝的请求频率可能会很⾼。token推荐加到请求头上,这样可以跟业务参数完全区分开来。
⼀般调⽤接⼝最常⽤的两种⽅式就是GET和POST。两者的区别也很明显,GET请求会将参数暴露在浏览器URL中,⽽且对长度也有限制。为了更⾼的安全性,所有接⼝都采⽤POST⽅式请求。
ip⽩名单是指将接⼝的访问权限对部分ip进⾏开放。这样就能避免其他ip进⾏访问攻击,设置ip⽩名单⽐较⿇烦的⼀点就是当你的客户端进⾏迁移后,就需要重新联系服务提供者添加新的ip⽩名单。设置ip⽩名单的⽅式很多,除了传统的防⽕墙之外,spring cloud alibaba提供的组件sentinel也⽀持⽩名单设置。为了降低api的复杂度,推荐使⽤防⽕墙规则进⾏⽩名单设置。
限流是为了更好的维护系统稳定性。使⽤redis进⾏接⼝调⽤次数统计,ip+接⼝地址作为key,访问次数作为value,每次请求value+1,设置过期时长来限制接⼝的调⽤频率。
使⽤aop全局记录请求⽇志,快速定位异常请求位置,排查问题原因。
在接⼝调⽤过程中,可能会涉及到订单号等敏感数据,这类数据通常需要脱敏处理,最常⽤的⽅式就是加密。加密⽅式使⽤安全性⽐较⾼的RSA⾮对称加密。⾮对称加密算法有两个密钥,这两个密钥完全不同但⼜完全匹配。只有使⽤匹配的⼀对公钥和私钥,才能完成对明⽂的加密和解密过程。
⼆幂等性问题
幂等性是指任意多次请求的执⾏结果和⼀次请求的执⾏结果所产⽣的影响相同。说的直⽩⼀点就是查询操作⽆论查询多少次都不会影响数据本⾝,因此查询操作本⾝就是幂等的。但是新增操作,每执⾏⼀次数据库就会发⽣变化,所以它是⾮幂等的。
幂等问题的解决有很多思路,这⾥讲⼀种⽐较严谨的。提供⼀个⽣成随机数的接⼝,随机数全局唯⼀。
调⽤接⼝的时候带⼊随机数。第⼀次调⽤,业务处理成功后,将随机数作为key,操作结果作为value,存⼊redis,同时设置过期时长。第⼆次调⽤,查询redis,如果key存在,则证明是重复提交,直接返回错误。
三数据规范问题
⼀套成熟的API⽂档,⼀旦发布是不允许随意修改接⼝的。这时候如果想新增或者修改接⼝,就需要加⼊版本控制,版本号可以是整数类型,也可以是浮点数类型。⼀般接⼝地址都会带上版本号,:port//v1/list。
⼀个⽜逼的API,还需要提供简单明了的响应值,根据状态码就可以⼤概知道问题所在。我们采⽤http的状态码进⾏数据封装,例如200表⽰请求成功,4xx表⽰客户端错误,5xx表⽰服务器内部发⽣错误。状态码设计参考如下:
分类描述
1xx信息,服务器收到请求,需要请求者继续执⾏操作
java加密方式有哪些2xx成功
3xx重定向,需要进⼀步的操作以完成请求
4xx客户端错误,请求包含语法错误或⽆法完成请求
5xx服务端错误
状态码枚举类:
public enum CodeEnum {
// 根据业务需求进⾏添加
SUCCESS(200,"处理成功"),
ERROR_PATH(404,"请求地址错误"),
ERROR_SERVER(505,"服务器内部发⽣错误");
private int code;
private String message;
CodeEnum(int code, String message) {
}
public int getCode() {
return code;
}
public void setCode(int code) {
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
}
}
为了⽅便给客户端响应,响应数据会包含三个属性,状态码(code),信息描述(message),响应数据(data)。客户端根据状态码及信息描述可快速知道接⼝,如果状态码返回成功,再开始处理数据。
响应结果定义及常⽤⽅法:
public class R implements Serializable {
private static final long serialVersionUID = 793034041048451317L;
private int code;
private String message;
private Object data = null;
public int getCode() {
return code;
}
public void setCode(int code) {
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
}
public Object getData() {
return data;
}
/**
* 放⼊响应枚举
*/
public R fillCode(CodeEnum codeEnum){
this.Code());
this.Message());
return this;
}
/**
* 放⼊响应码及信息
*/
public R fillCode(int code, String message){
this.setCode(code);
this.setMessage(message);
return this;
}
/**
* 处理成功,放⼊⾃定义业务数据集合
*/
public R fillData(Object data) {
this.setCode(Code());
this.setMessage(Message());
this.data = data;
return this;
}
}
总结
本篇⽂章从安全性、幂等性、数据规范等⽅⾯讨论了API设计规范。除此之外,⼀个好的API还少不了⼀个优秀的接⼝⽂档。接⼝⽂档的可读性⾮常重要,虽然很多程序员都不喜欢写⽂档,⽽且不喜欢别⼈不写⽂档。为了不增加程序员的压⼒,推荐使⽤swagger或其他接⼝管理⼯具,通过简单配置,就可以在开发中测试接⼝的连通性,上线后也可以⽣成离线⽂档⽤于管理API。
点关注、不迷路
如果觉得⽂章不错,欢迎关注、点赞、收藏,你们的⽀持是我创作的动⼒,感谢⼤家。
如果⽂章写的有问题,请不要吝啬,欢迎留⾔指出,我会及时核查修改。
如果你还想更加深⼊的了解我,可以搜索「Java旅途」进⾏关注。回复「1024」即可获得学习视频及精美电⼦书。每天7:30准时推送技术⽂章,让你的上班路不在孤独,⽽且每⽉还有送书活动,助你提升硬实⼒!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论