大家好,我是鱼皮,前段时间在 星球 里看到了一位小伙伴分享的文章《API 接口设计最佳实践》,我也读了一遍,写的确实好,给大家分享一下,相信对后端开发的小伙伴会很有帮助。
作者:InfoQ Man
Token 是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个 Token 便将此 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次带上用户名和密码。
Token 的值一般用 UUID(算法比较著名的有雪花算法),当服务端接收到客户端请求后会生成 Token(一串字符,如 etye0fgkgk4ca2ttdsl0ae9a5dd77471fgf),然后将 Token 作为 key 将一些和 Token 关联的信息作为 value 保存到如 Redis 缓存数据库中,同步把该 Token 返回给客户端;后续该客户端的请求都需要带上这个 Token,服务器收到请求后就会去缓存服务器中匹配这个 Token 是否存在,存在则调用接口,不存在返回接口错误。
API Token(接口令牌): 一般用于访问不需要用户登录的接口,如登录、注册、一些基本数据的获取(如信用卡官网的如信用卡费率相关信息)等。获取接口令牌需要拿 appId、timestamp 和 sign 来换;其中该 sign 值一般是把 timestamp、key 和对应的参数先进行字母排序再进行 MD5 加密(有时候会加盐),即 sign=MD5(排序(timestamp+key+参数));
USER Token(用户令牌): 用于访问需要用户登录之后的接口,如:获取我的基本信息、保存、修改、删除等操作。获取用户令牌需要拿用户名和密码来换。
在设计初期需要明确双方的通讯协议是 TCP、HTTP、RPC,一般针对比较敏感的交易或者行业(如金融业),建议使用 HTTPS 协议以确保数据交互的安全。
建议采用 Restful 的风格,一般采用这样的格式:控制器名/方法名。具体请参考以下例子:
POST /recommend/cardlist
APP 后台逻辑总是处于变化当中,但是 APP 端(如安卓和 ios)因为涉及到应用市场的审核问题,还有这些 2C 端的 APP 应用存在版本碎片化的问题,因此后台暴露的接口需要在一段时间内支持不同版本的接口,一般方法是通过 Nginx 通过配置过滤根据接口的不同版本进行路由分发。
一般来说,接口的版本管理一般有以下两种方法:
POST v1/recommend/cardlist
Nginx的路由分发:
server {
listen 80;
server_name vip.com;
location /v1/ {
proxy_pass http://129.0.0.1:8001/;
proxy_redirect http://129.0.0.1:8001/ /v1/;
proxy*set*header Host $host;
}
location /v2/ {
proxy_pass http://129.0.0.1:8002/;
proxy_redirect http://129.0.0.1:8002/ /v2/;
proxy*set*header Host $host;
}
}
server {
listen 8001;
allow 129.0.0.1;
deney all;
server_name vip.com;
root vip.com/v1/;
}
server {
listen 8002;
allow 129.0.0.1;
deney all;
server_name vip.com;
root vip.com/v2/;
}
为调用你的系统分配一个 ID 和 key,针对每个请求对 ID 和 key 进行校验,避免在企业内网中的其他系统只要知道接口被可以随意调用。
返回数据尽量统一规范,务必包括:返回码、返回信息、数据。
{ "code" : 0, "content" : "string", <- 这里为 JSON "message" : "string" }
当我们开发的接口需要暴露到公网,这样的风险跟我们在企业内网暴露给其他系统调用的风险是不可同日而语的。其中有很多风险需要我们一一解决。以下仅提供能想到的:
目前业界老生常谈就是对称加密和非对称加密。
对称加密:对称密钥在加密和解密的过程中使用的密钥是相同的,常见的对称加密算法有 DES,AES;优点是计算速度快,缺点是在数据传送前,发送方和接收方必须商定好秘钥,然后使双方都能保存好秘钥,如果一方的秘钥被泄露,那么加密信息也就不安全了;
非对称加密:服务端会生成一对密钥,私钥存放在服务器端,公钥可以发布给任何人使用;优点就是比起对称加密更加安全,但是加解密的速度比对称加密慢太多了;广泛使用的是 RSA 算法;
目前主流的做法是在传输层使用 https 协议,http 和 tcp 之间添加一层加密层(SSL 层),这一层负责数据的加密和解密。https 协议则是巧妙的利用上述两种对称加密方法;浅显一点说就是客户端和服务端建立三次握手连接过程中通过交换双方非对称公钥,接着使用对方非对称公钥加密双方约定好的对称密钥,这样就只有双方有这个对称密钥(这样的非对称加密可以保证很安全的把对称密钥给到对方)。后续双方的报文沟通就可以使用该对称密钥进行加解密(这样的对称加密可以保证请求报文可以快速被解密处理,并在处理后被快速加密响应回去)。
这个时候我们需要对数据进行加签,数据签名平时用得比较多的是 MD5,即将需要提交的数据通过某种方式组合和一个字符串,然后通过 MD5 生成一段加密字符串,这段加密字符串就是数据包的签名。具体请看以下的图。
如果加密数据被抓包后被用于重放攻击,我们怎么办?这个时候我们可以把解密后的 URL 参数中的时间戳与系统时间进行比较,如果时间差超过一定间距(如 5 分钟)即认为该报文被劫持并返回错误。但是,务必保证该时间戳的超时时间一定要跟 sign 保存的有效时间一致。
客户端在第一次访问服务端时,服务端将 sign 缓存到 Redis 中并把有效时间设定为跟时间戳的超时时间一致;如果有人使用同一个 URL 再次访问,如果发现缓存服务器中已经存在了本次的 sign,则拒绝服务;如果在 Redis 中的 sign 失效的情况下,有人使用同一个 URL 再次访问,则会被时间戳超时机制拦截。这样的话,就可以避免 URL 被别人截获后的重放攻击。
整个流程如下: 1、客户端通过用户名密码登录服务器并获取 Token 2、客户端生成时间戳 timestamp,并将 timestamp 作为其中一个参数 3、客户端将所有的参数,包括 Token 和 timestamp 按照自己的算法进行排序加密得到签名 sign 4、将 token、timestamp 和 sign 作为请求时必须携带的参数加在每个请求的 URL 后边(http://url/request?token=123×tamp=123&sign=123123123) 5、服务端写一个过滤器对 token、timestamp 和 sign 进行验证,只有在 token 有效、timestamp 未超时、缓存服务器中不存在 sign 三种情况同时满足,本次请求才有效。
另外,一般会在 URL 参数上加上随机数(即所谓的加盐)并与 6.3 的时间戳机制组合使用以便提升防重复提交攻击。
针对同一个 IP 在短时间内频繁请求的,可以通过 Nginx 进行过滤,同步可以在 Nginx 部署动态黑名单(即 IP 实时更新到黑名单库),这样可以防控少量的 DDOS。但受限于判断黑名单需要考虑多维度的信息,一般我们的 Nginx 尽量只做同一 IP 校验,更多维度的黑名单校验可以通过厂商去解决。
这里的数据合法性校验主要指的是数据格式校验和业务规则校验。
定义:在计算机中,表示对同一个过程应用相同的参数多次和应用一次产生的效果是一样,这样的过程即被称为满足幂等性。
具体的解决方案有 token 机制、分布式锁、状态机等方案;这里引用一下之前看到的一篇文章,写得比较详细:https://blog.csdn.net/u011635492/article/details/81058153
一家公司的每个系统都会有各种各样的接口,但是大部分公司,特别是传统行业的公司的所谓接口文档更多是当每个系传统的 word 文本格式,这种传统的格式有着人尽皆知的痛点:
为了解决上述的问题,需要建立一套行之有效的接口管理体系,该体系的目标是:
业界有很多不同的 API 接口管理平台,如去哪儿网的 YAPI 平台、阿里某团队开发的 RAP 平台、Swagger、easyAPI 等等。各位小伙伴可以分享一下自己对这些平台的使用感受~