The design of API

July 25, 2017 by dc

每个人好像都会写一篇API设计

请求签名

API会给调用方分配一堆调用密钥,例如api key和api secret。api key标示调用方身份,api secret一般在计算签名时使用。

请求签名的目的是让API知道这个请求是来自哪个调用方、传输时请求是否被篡改。

  • 计算方法:HMAC-SHA1、SHA1、MD5等
  • 签名内容:请求参数、URL、随机字符串或timestamp、http method

支付宝和微信的请求参数中包含业务参数通信参数。业务参数即调用的某个接口名称、接口所需的各参数值;通信参数即调用方是谁、用那种签名方式、本次请求的签名是什么等。

计算方法一般是将所有请求参数按照参数名称的字典顺序连接起来,再拼接上api secret,计算,最终用base64编码表示。

这样计算理解方便,在使用时会减少一些不必要的麻烦:

  • 参数都是通过POST方法发送到API网关上而不是某个具体的接口endpoint,调用接口在参数中指定。
  • 参数都是key=value形式,在拼接计算时不会出现二义性。我设计过一个API,请求数据全是json,POST、PUT等方法还好说,GET、DELETE这些方法只有query string可传参数,而且json类型中有复杂的array和object,调用方和API都需要按照统一的序列化方法计算签名(例如是否要去掉所有json中的空格?),否则调试起来会增加沟通时间。

response

  • response header中应包含request id等帮助调试的信息。

  • 应当充分使用标准协议状态码,如HTTP response status code,成功状态2xx,大部分操作应为200,创建资源接口应返回201,删除资源接口应返回204等。

  • response应为一个dict对象,不应该有循环引用,如果需要可以转为json或者其他格式。

  • dict对象中应包含msg字段,表示这次操作成功或失败的原因等,最好不出现状态码,出错原因都可以在msg中交代清楚,以及后续需要用户的操作也可交代清楚。

  • Bad request应该返回具体原因,例如某个字段未填写,应给出足够信息供前端选择使用,下游系统可以拿到出错的字段名或input的name等。

认证

两种认证方式

应使用基于token的认证方式。

与传统web的session认证方式不同在于基于token可做到API是无状态服务,无状态意味着可以水平扩展。无状态表现在API服务只需通过token即可确定客户端是否可以访问需授权资源,而不是需要通过集中的认证后才确定客户端是经过授权的,可以访问某些资源。

具体的,传统web基于cookie的session,客户端使用正确的账号密码登录系统后,系统将一段cookie发送给客户端,同时在服务端将用户身份和cookie的映射关系记录下,当客户端拿着这段cookie访问某些资源时,服务端首先验证cookie是否有效,如果有效那么客户端的身份是谁也就被确定,接下来即可访问某些资源。所以只有存储了cookie和身份映射关系的服务器才能完成用户身份识别认证,如果想要通过增加服务器来扩展接入能力(水平扩展),则需要将映射关系放在多台服务器上,否则用户访问的服务器如果不存在映射关系,则该客户端就无法访问某些资源了。

在访问量有限的情况下,可以将映射关系存储在redis中,水平扩展接入服务器,认证时访问redis完成。也可将API服务改造成无状态模式,即API本身即可确认客户端是否有权限访问,这样即可水平扩展。

要设计无状态的API服务,token中则需要包含足够的信息可以让API服务完成身份验证,例如JWT的方案:一个token包含三部分内容:签名算法、payload、签名。payload中有token的过期时间,API拿到token后可先验证token的签名是否正确,如果正确则表示该token内容未被篡改,可以相信payload中的数据,如果payload中过期时间未到达,则表示该token还在生效中,即可访问某些资源。

因为无状态,缺点就是无法中途吊销token,所以token的有效期应设置得尽量短。

token存储

存储方式有web storagecookie,前者包含local storagesession storage

如果存在web storage中,同一个域名下的js可以访问,如果有跨站脚本会使得token不安全。跨站脚本是一段js脚本但被放在web相同域名下的某网页中,例如某些需要用户输入的地方未过滤html标签,可能被写入一段js脚本,该脚本即可访问web storage

如果存在cookie中,可添加httponly来防止js访问该cookie,可以添加secure来使得cookie通过https发送。但需要注意CSRFCSRF是指你在浏览其他恶意网站时,可能会让你

鉴权

鉴权和认证看起来很像,但这是另外一个方面。

认证要解决的问题是调用方的身份是否有效,鉴权要解决的问题是调用方是否有权限访问、请求。

鉴权的前提是有效身份,而有效身份并不意味着就有调用的权限。

为了简化可以只保留认证,即身份合法权限都一致。

流控

良好的API设计都有流控,流控是为了让请求尽量缓和,不至于因为某一个调用量特别大占用过多资源以至于影响了其他API或整个系统的可用性。

一般在response中告知调用方限制总量为多少、还有多少调用量剩余,当超过调用量时会告知调用方需等待多久后方可再次调用,目前没看到国内有较好的设计,可参考Github的API设计。


https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

http://insights.thoughtworkers.org/do-not-use-jwt-anymore/

© 2018 | Follow on Twitter | Hucore theme & Hugo