最近需要搭一个第三方验证的服务器。这个想法之前就有,但是当时水平粗浅,也只能看个一知半解,没有理解透彻。趁这个机会好好学一学 OAuth 2.0 认证框架。
一些小问题
什么是 OAuth 2.0
OAuth 是一套第三方认证框架,即认证服务器和资源服务器分开,用户向认证服务器授权,资源服务器才有权使用认证服务器认证用户身份,认证通过后用户才有权使用资源服务器中的权限资源。2.0 是目前的版本号。
以下的 OAuth 代指 OAuth 2.0 框架。
目前微信、QQ、新浪微博、人人网等众多网站都支持这样的第三方登录,基本都是使用的 OAuth 框架。其中 Github 的较为标准。所以我们可以以此为例。
为什么要用第三方登录
对于小型应用而言,希望降低使用者的门槛,无需经过复杂的注册;另一方面可以直接使用腾讯、新浪等大网站的客户资源;性能和成本上讲也省掉了自己维护大量用户数据的烦恼。
对于大型网站而言,方便自己解耦,可以更方便地扩展新的应用;也可以吸引用户粘性,让用户更多的应用关联自己的用户资源;同时这种关联信息也是日后数据挖掘的一项原料。
当然,上面的这些其实都是我瞎猜的。
自己的项目之所以要将授权服务器拆开纯粹是为了解耦,可以多应用使用相同的用户资源而不会建立多余数据库连接。
OAuth 基本定义
角色
OAuth 框架定义了四种角色:
- 资源拥有者(Resource Owner)
- 指权限资源的拥有者,当拥有者是一个人的时候,我们称为用户。(权限资源指需要经过验证,确认具有权限的主体才能获取的资源)
- 资源服务器(Resource Server)
- 管理权限资源,接受和响应使用令牌(token)的请求。
- 客户端(Client)
- 经过资源拥有者授权的、代表其发送权限资源请求的应用。(客户端可以泡在服务器、用户电脑或是其它任何设备上)
- 授权服务器(Authorization Server)
- 在验证资源拥有者身份和获得授权后向客户端分配令牌(token)。
协议流
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
(A) Authorization Request
客户端需要资源拥有者授权。授权请求可以直接对资源拥有者请求,或者更好的做法是通过授权服务器作为媒介。
(B) Authorization Grant
客户端收到来自资源拥有者的授权许可。许可的格式通常使用以下定义的四种授权模式的一种(当然你也可以自行定义授权模式)。具体使用何种模式取决于客户端使用了哪种且授权服务器是否支持。
(C) Authorization Grant
客户端向授权服务器请求一个令牌(token)表示授权许可。
(D) Access Token
授权服务器验证客户端并确认授权许可。如果授权许可通过,那么分配给客户端一个 token。
(E) Access Token
客户端通过 token 验证身份并从资源服务器请求资源。
(F) Protected Resource
资源服务器验证 token 有效性后处理请求。
授权模式
有四种常见的授权模式:
授权码模式
这种授权模式将授权服务器作为客户端和资源拥有者的中间媒介。用户不向客户端直接授权,而是将用户指引到授权服务器,然后将得到授权码的用户定向回客户端(原文 1.3.1 中提到通过 user-agent 的方式引导,我没有太懂是什么意思,欢迎看懂的小伙伴给我解答一下)
简化模式
简化模式是为使用脚本语言(例如 JavaScript)的客户端优化设计的一种授权模式。简化模式中不分配授权码,而是直接分配 token(作为资源拥有者的身份验证结果)。因为没有中间验证信息(例如授权码),所以这种授权模式称为简化模式。
在简化模式中,授权服务器并不验证客户端就直接分配了 token。在一些情况下,客户端身份可以通过重定向到返回 token 的 URI 来验证客户端。这个 token 可能会被暴露给用户或者其他用户 UA 层面的应用。
简化模式对于一些比如浏览器客户端可以提高效率,当然也会导致一些安全上的问题。
第一点是可能 token 被抓,所以必须使用 TLS(即使用 HTTPS)。
第二点是可能存在“点击挟持”(clickjacking),即使用隐形的不可见的 button 覆盖在授权页面上,让用户在不知情情况下误授权。所以本地应用应当使用外部浏览器而不是应用内的嵌入浏览器。
密码验证模式
密码模式即直接使用账号密码(或其他验证信息)登录。只应当用于用户对客户端高度信任(例如是设备操作系统的一部分)且其他授权模式都不可用的情况。
即使使用这种模式,客户端也不应当存储账号密码,而是每次都使用验证信息去交换 token。
客户端证书模式
将客户端证书(或其他形式的客户端凭证)看做授权凭证。在已经授权的资源范围内,客户端可以被看做用户本人,拥有获取权限资源的权限。
授权令牌(token)
简单来说,token 就是表示授权的一个临时证明,通常是一个字符串。这个字符串通常对客户端是不透明(opaque)的(根据我的理解,应该指的是看不见)。用户授权后,资源服务器和授权服务器才可以通过 token 来验证对某一指定域的权限。当超越已授权的权限时,可能需要提供额外的验证信息来获得权限。
token 本身就已经足够表明授权者身份。
参考文献
The OAuth 2.0 Authorization Framework - Internet Engineering Task Force (IETF)