App架构设计:接⼝的设计
安全机制的设计
现在,⼤部分App的接⼝都采⽤RESTful架构,RESTFul最重要的⼀个设计原则就是,客户端与服务器的交互在请求之间是⽆状态的,也就是说,当涉及到⽤户状态时,每次请求都要带上⾝份验证信息。实现上,⼤部分都采⽤token的认证⽅式,⼀般流程是:
⽤户⽤密码登录成功后,服务器返回token给客户端;
客户端将token保存在本地,发起后续的相关请求时,将token发回给服务器;
服务器检查token的有效性,有效则返回数据,若⽆效,分两种情况:
token错误,这时需要⽤户重新登录,获取正确的token
token过期,这时客户端需要再发起⼀次认证请求,获取新的token
然⽽,此种验证⽅式存在⼀个安全性问题:当登录接⼝被劫持时,⿊客就获取到了⽤户密码和token,后续则可以对该⽤户做任何事情了。⽤户只有修改密码才能夺回控制权。
如何优化呢?第⼀种解决⽅案是采⽤HTTPS。HTTPS在HTTP的基础上添加了SSL安全协议,⾃动对数据进⾏了压缩加密,在⼀定程序可以防⽌监听、防⽌劫持、防⽌重发,安全性可以提⾼很多。不过,SSL也不是绝对安全的,也存在被劫持的可能。另外,服务器对HTTPS 的配置相对有点复杂,还需要到CA申请证书,⽽且⼀般还是收费的。⽽且,HTTPS效率也⽐较低。⼀般,只有安全要求⽐较⾼的系统才会采⽤HTTPS,⽐如银⾏。⽽⼤部分对安全要求没那么⾼的App还是采⽤HTTP的⽅式。
我们⽬前的做法是给每个接⼝都添加签名。给客户端分配⼀个密钥,每次请求接⼝时,将密钥和所有参数组合成源串,根据签名算法⽣成签名值,发送请求时将签名⼀起发送给服务器验证。类似的实现可参考OAuth1.0的签名算法。这样,⿊客不知道密钥,不知道签名算法,就算拦截到登录接⼝,后续请求也⽆法成功操作。不过,因为签名算法⽐较⿇烦,⽽且容易出错,只适合对内的接⼝。如果你们的接⼝属于开放的API,则不太适合这种签名认证的⽅式了,建议还是使⽤OAuth2.0的认证机制。
我们也给每个端分配⼀个appKey,⽐如Android、iOS、三端,每个端分别分配⼀个appKey和⼀个密钥。没有传appKey的请求将报错,传错了appKey的请求也将报错。这样,安全性⽅⾯⼜加多了⼀层防御,同时也⽅便对不同端做⼀些不同的处理策略。
另外,现在越来越多App取消了密码登录,⽽采⽤⼿机号+短信验证码的登录⽅式,我在当前的项⽬中也采⽤了这种登录⽅式。这种登录⽅式有⼏种好处:
不需要注册,不需要修改密码,也不需要因为忘记密码⽽重置密码的操作了;
⽤户不再需要记住密码了,也不怕密码泄露的问题了;
相对于密码登录其安全性明显提⾼了。
接⼝数据的设计
接⼝的数据⼀般都采⽤JSON格式进⾏传输,不过,需要注意的是,JSON的值只有六种数据类型:
Number:整数或
String:字符串
Boolean:true 或 false
Array:数组包含着⽅括号[]中
Object:对象包含在⼤括号{}中
Null:空类型
所以,传输的数据类型不能超过这六种数据类型。以前,我们曾经试过传输Date类型,它会转为类似于”2016年1⽉7⽇ 09时17分42秒GMT+08:00″这样的字符串,这在转换时会产⽣问题,不同的解析库解析⽅式可能不同,有的可能会转乱,有的可能直接异常了。要避免出错,必须做特殊处理,⾃⼰⼿动去做解析。为了根除这种问题,最好的解决⽅案是⽤毫秒数表⽰⽇期。
另外,以前的项⽬中还出现过字符串的”true”和”false”,或者字符串的数字,甚⾄还出现过字符串的”null”,导致解析错误,尤其是”null”,导致App奔溃,后来查了好久才查出来是该问题导致的。这都是因为服务端对数据没处理好,导致有些数据转为了字符串。所以,在客户端,也不能完全信任服务端传回的数据都是对的,需要对所有异常情况都做相应处理。
服务器返回的数据结构,⼀般为:
{    code:0    message: "success"    data: { key1: value1, key2: value2, ... } }
code: 状态码,0表⽰成功,⾮0表⽰各种不同的错误
message: 描述信息,成功时为”success”,错误时则是错误信息
data: 成功时返回的数据,类型为对象或数据
不同错误需要定义不同的状态码,属于客户端的错误和服务端的错误也要区分,⽐如1XX表⽰客户端的错误,2XX表⽰服务端的错误。这⾥举⼏个例⼦:
0:成功
100:请求错误
101:缺少appKey
102:缺少签名
103:缺少参数
200:服务器出错
201:服务不可⽤
202:服务器正在重启app接口测试工具
错误信息⼀般有两种⽤途:⼀是客户端开发⼈员调试时看具体是什么错误;⼆是作为App错误提⽰直接展⽰给⽤户看。主要还是作为App错误提⽰,直接展⽰给⽤户看的。所以,⼤部分都是简短的提⽰信息。
data字段只在请求成功时才会有数据返回的。数据类型限定为对象或数组,当请求需要的数据为单个对象时则传回对象,当请求需要的数据是列表时,则为某个对象的数组。这⾥需要注意的就是,不要将data传⼊字符串或数字,即使请求需要的数据只有⼀个,⽐如token,那返回的data应该为:
// 正确
data: { token: 123456 }
// 错误
data: 123456
接⼝版本的设计
接⼝不可能⼀成不变,在不停迭代中,总会发⽣变化。接⼝的变化⼀般会有⼏种:
数据的变化,⽐如增加了旧版本不⽀持的数据类型
参数的变化,⽐如新增了参数
接⼝的废弃,不再使⽤该接⼝了
为了适应这些变化,必须得做接⼝版本的设计。实现上,⼀般有两种做法:
每个接⼝有各⾃的版本,⼀般为接⼝添加个version的参数。
⼤部分情况下会采⽤第⼀种⽅式,当某⼀个接⼝有变动时,在这个接⼝上叠加版本号,并兼容旧版本。App的新版本开发传参时则将传⼊新版本的version。
如果整个接⼝系统的根基都发⽣变动的话,⽐如微博API,从OAuth1.0升级到OAuth2.0,整个API都进⾏了升级。
有时候,⼀个接⼝的变动还会影响到其他接⼝,但做的时候不⼀定能发现。因此,最好还要有⼀套完善的测试机制保证每次接⼝变更都能测试到所有相关层⾯。
写在最后
关于接⼝设计,暂时想到的就这么多了。各位看官看完觉得有遗漏或有哪些需要优化的欢迎提出⼀起讨论。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。