鉴于微博,QQ,微信等开发平台的影响,互联网界的工程师都知道 OAuth 协议,对 Token 存储用户信息的机制有所了解,却很少有人提及两方 OAuth 这个概念。
两方 OAuth
说到 OAuth 协议,潜意识里都会想到涉及认证,客户,和服务器,进而是认证服务器,客户端应用和资源服务器三方。
现在通用的 OAuth 协议是 OAuth2.0,是一个互联网开放授权协议,用于规范资源服务器,客户端应用,授权服务器三者的职责,实现客户端应用在不直接获取到普通用户的用户名和密码的前提下,访问用户的私有资源。
在实际应用开发过程中,我们的应用复杂性没有达到一定规模时,应用程序只涉及到客户端 APP 和服务器端中心云服务的认证和业务处理。我们可以对 OAuth2.0 协议进行简化,演变为两方 OAuth。
Auth-history.png
上图[Auth-history.png]是几个服务器客户端验证的阶段。
OAuth 协议更多的可以理解为是一个宽泛的协议,在工程应用方面只关注规范,不要求细节。
最初的 OAuth 协议是服务器根据 HTTP 协议头的 Authorization 字段验证客户端身份。
而如今逐渐演变成使用 Token 的方式标记客户端身份,存储用户状态信息,至于 Token 如何生成,在 HTTP 协议中如何传输,并没有过多硬性要求。
消费者 APP
在 OAuth 协议体系中,消费者是指开发者开发的 APP,这里的 APP 更是一个广义的概念,不局限在安卓和 iOS 应用这两种类型。PC 网站,移动端 WebView 也被认为是 APP
服务端,各种云平台如何识别这些应用,借助于应用编号和应用密钥机制,应用编号就是 APP_ID,或者 APP_KEY,用于唯一识别应用。
密钥就是 APP_SERCRET,用于请求签名等安全算法的入参,和服务器端验证的凭据。
“APP_KEY 和 APP_SERCRET 的分配和管理是实现两方 OAuth 的第一步
读到这里,或许你有疑问了,上文说到的不同 APP,无非是安卓,iOS,WebView,我们何不定义不同的枚举来标明不同的客户端,甚至可以使用 User-Agent 判断。
“1 PC,2 安卓 3 iOS 4 微信
这样的分类可以解决吗,答案是很难。
实际上这是两个概念,从操作系统的角度划分更多的是一种数据来源渠道的概念,而 APP_KEY 的本质属性是接入云端平台的开发者应用标记。
Token 生命周期
以下是一个简版的 Token 生命周期模型
分配
Token 由客户端发起,服务器云端负责分配,典型的场景,在用户登录成功后分配,一定有效期内过期,支持每次请求不一致,token 滑动过期机制。默认 30 分钟失效。
生成规则
使用用户标记 userid 应用编号。md5(userid 盐钥 时间戳) 盐值固定。生成规则可以根据项目个性化调整,Token 值不可逆。
存储
服务器端 key value 形式存储到 redis 中,key 为 token,value 为加密值 客户端 Token,按需存储在本地,后续接口调用时使用。
失效
过期时间内失效,如初始分配 60 分钟,那 60 分钟后自动生效。退出时,需要调用接口,删除 Token。
这里会引出一个思考
“退出功能需要网络支持吗?
这个问题的缘由是我发现有些工程师,退出功能是这么做的,页面跳转,清除本地 Token。
Token 代表的是用户状态,这种状态代表的是客户端与服务端的一种关联关系,退出功能是切断这种关联。
HTTP 是无状态的,单纯的做请求响应,而业务必须是有状态的,否则业务无法流转和推进,这种状态交给 Token 负责,二者是如何关联的。是 Token 设计中需要考量的。
“一个相对完整的 Token 落地机制是实现两方 OAuth 的第二步
签名算法
签名(Sign)用于保证数据的真实性和完整性,从时效,合法性,频率几个维度处理。
对所有待签名参数按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式(即 key1=value1&key2=value2…)拼接成字符串 string1。
这里需要注意的是所有参数名均为小写字符,最后加&secret=app_secret;对 string1 作签名算法,字段名和字段值都采用原始值不进行 URL 转义。
具体签名算法为 sign = md5(string)。除登录外所有接口,由服务端决定开启或者关闭签名验证。业界叫验签。
签名就用到了 上文提到的APP_SECRET,服务器和客户端同时使用APP_SECRET 来生成Sign,校验请求是否合法。
sign-token.png
设计要点
采用前后端分离,将接口参数分为系统级别参数和业务级别参数。系统级参数主要包括 app_key,timestamp,token,os_type,sign,主要置于 HTTP 请求头位置。业务参数基于业务需求,采用 POST 或者 GET 方法按需传递。
检测请求超时
通过客户端时间戳 timestamp,来确定真正的客户端发起时间。服务器端检测时间差,客户端请求时间与服务器时间的差值与超时时间做对比。例如,我们可以约定时间差大于 5 分钟间隔的请求为无效请求,或者超时请求。
下文中罗列一些这种设计的优势
识别终端
使用 App-Key 的方式识别应用终端,企业内部不同的应用,由云端服务统一管理。
场景 1
进行个性化配置 ,来自于微信公众号的登录用户的过期时长设置为一天,而来自于 PC 官网的用户的登录过期时长设置为一周。场景 2
公司某个产品线的普通版和定制版的区分,使用 App-Key,对功能的简易复杂度,收费模式做梯度设置。
场景 3
应用于 SASS 应用识别,多租户数据隔离,在数据库对应用数据做隔离。进而为相关的运营数据分析提供支持。
其它设计规约
1 后台配置关闭启用开关,开发阶段可以关闭验证,建议开启。
2 启用验证后,前端需要有公共方法计算请求签名。后台需要增加安全验证模块验证请求合法性,负责登录过期失效检测,参数完整性和权限验证。
3 安全方面关注安全弹性,安全验证级别通过 timestamp, sign, token 参数,三个维度配合,逐层升级。
通过增加验证条件和复杂性增强安全级别。安全级别做到合理即可,没有一概而论。
总结
本文给出了一套基于两方 OAuth 方式的云端,客户端服务设计模型。在应用服务不复杂,业务场景允许的前提下,使用两个 OAuth 方式。
伴随着业务发展,可以逐步演化为基于三方身份的 OAuth 协议工程实现。
Token 机制和签名机制也可以 独立分层,与业务应用分离,演化为网关系统。这样的设计符合架构领域的简单,演化原则,是一种很实惠的应用架构方案。