web:http://oauth.net/2/
rfc:http://tools.ietf.org/html/rfc6749
doc:http://oauth.net/documentation/
code:http://oauth.net/code/
OAuth2.0授权类型
OAuth2.0协议定义了用于获得授权的四种主要授权类型。
Authorization code
标准的Server授权模式,非常适合Server端的Web应用。一旦资源的拥有者授权访问他们的数据之后,他们将会被重定向到Web应用并在URL的查询参数中附带一个授权码(code)。在客户端里,该code用于请求访问令牌(access_token)。并且该令牌交换的过程是两个服务端之前完成的,防止其他人甚至是资源拥有者本人得到该令牌。另外,在该授权模式下可以通过refresh_token来刷新令牌以延长访问授权时间。
Implicit Grant
该模式是所有授权模式中最简单的一种,并为运行于浏览器中的脚本应用做了优化。当用户访问该应用时,服务端会立即生成一个新的访问令牌(access_token)并通过URL的#hash段传回客户端。这时,客户端就可以利用JavaScript等将其取出然后请求API接口。该模式不需要授权码(code),当然也不会提供refresh token以获得长期访问的入口。
Resource Owner Password Credentials
这种模式要求用户提供用户名和密码来交换访问令牌(access_token)。该模式仅用于非常值得信任的用户,例如API提供者本人所写的移动应用。虽然用户也要求提供密码,但并不需要存储在设备上。因为初始验证之后,只需将OAuth的令牌记录下来即可。如果用户希望取消授权,因为其真实密码并没有被记录,因此无需修改密码就可以立即取消授权。token本身也只是得到有限的授权,因此相比最传统的username/password授权,该模式依然更为安全。
Client Credentials
一种基于APP的密钥直接进行授权,因此APP的权限非常大。它适合像数据库或存储服务器这种对API的访问需求
之前做微信公众平台,基本流程参照文档,也没有太注意OAuth太多细节,最近在与携程会员对接,陆陆续续折腾好几天吧,就花了一点时间研究了下OAuth协议,感觉OAuth大行其道路,另外OAuth与OpenID是比较容易混淆的两个东西,OpenID设计的目的趋向与身份校验,OAuth设计的目的是对用户的资源授权。现阶段一般互联网公司应该都支持OAuth2协议,如新浪,阿里,腾讯等,特点是都拥有大量的用户群,来等待其他开发者与第三方应用的接入,如现在比较火爆的微信公众平台。
什么是OAuth2.0
OAuth2.0是一个开放协议,允许用户让第三方应用以安全且标准的方式获取该用户在某一网站、移动或桌面应用上存储的私密的资源(如用户个人信息、照片、视频、联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。
OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容。
上面是比较官方的描述,简单些就是OAuth2是由OAuth1.0发展而来,两者不向后兼容,支持多平台设备。可以使用自己的账户与密码授权来让第三方应用在特定的时间内获得用户的资源信息。以新浪围脖为例,比如我习惯用微博登录一些社交网站,这样发现有意思的东西,可以快速分享到微博上,嗯,我经常这么干。按照流程一般先需要用户到新浪上去授权,登录成功后,在知乎上就可以获得微博上的头像与用户基本信息等。
OAuth2授权流程
在讲述OAuth2.0之前先得了解基本授权的基本术语:
资源拥有者(resource owner):能授权访问受保护资源的一个实体,如新浪微博用户 irving;
资源服务器(resource server):存储受保护资源,客户端通过access token请求资源,资源服务器响应受保护资源给客户端;存储着用户irving的微博等信息。
授权服务器(authorization server):成功验证资源拥有者并获取授权之后,授权服务器颁发授权令牌(Access Token)给客户端。
客户端(client):如新浪微博第三方应用,也可以是它自己的官方应用;其本身不存储资源,而是资源拥有者授权通过后,使用它的授权(授权令牌)访问受保护资源,然后客户端把相应的数据展示出来。“客户端”术语不代表任何特定实现(如应用运行在一台服务器、桌面、手机或其他设备)。
OAuth2授权模式
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
因为一般授权码的使用的比较多,客户端可能会使用简化模式或客户端模式,我这里只简述授权码模式,授权码流程
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
(A)用户授权访问认证服务器
(B)用户选择是否给予客户端授权
(C)如果给予授权,认证服务器(authorization service)将重定向(http状态码302)到A流程中给予的URL(redirect_uri),同时附上一个授权码(code)
(D)客户端收到授权码(code),向认证服务器(authorization service)申请令牌(发送post请求)。这个过程是在客户端的后台请求上完成的,对用户不可见
(E)认证服务器检查授权码(code)和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)以及失效时间(expires_in)
如新浪的授权流程图:
1、客户端从资源拥有者给予授权
比如我现在使用新浪微博的账户授权登录知乎。
GET /oauth/auth/sina?next=/oauth/account_callback HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/请求包含以下参数:
- response_type:表示授权类型,必选项,此处的值固定为"code"
- client_id:表示客户端的ID,必选项
- redirect_uri:表示重定向URI,可选项
- scope:表示申请的权限范围如 user,order,可选项
- state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
一般开发一个基于新浪微博app的第三方应用,需要申请appkey和appsecret分别对应OAuth2.0中的client_id与client_secret,可能一些服务端会扩展一些其他的名称,腾讯就是这个干的。
2、用户同意给予客户端授权
分两种情况,如果是没有微博登录成功的会话,就让用户登录,如上图如果之前我已经有了登录成功的会话了,这个时候就让用户确认同意授权(知乎好流氓,直接略了这一步,但是流程还是一样的),我用其他网站的联合登录截了个图如下。
CONNECT api.weibo.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Connection: keep-alive
Connection: keep-alive
Host: api.weibo.com:443
后面我使用Fiddler抓了下包流程还是一样的,还是一样会302跳转,另外发现还使用了socketio,貌似服务端有基于NodeJS的服务。
GET /socket.io/1/?t=1417252232035 HTTP/1.1
Host: comet.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Origin: http://www.zhihu.com
Connection: keep-alive
返回:
HTTP/1.1 200 OK
Server: zhihu_nginx
Date: Sat, 29 Nov 2014 09:11:19 GMT
Content-Type: text/plain
Connection: keep-alive
Vary: Accept-Encoding
Access-Control-Allow-Origin: http://www.zhihu.com
Access-Control-Allow-Credentials: true
Content-Length: 36rfmX2x_OC9aKyzZAGh3c:60:60:websocket
GET /oauth/auth/request_sina_token?next=%2Foauth%2Faccount_callback&state=78db9a8b58b2dab6970d4520907ad629&code=879c3a9aabdf5dfe69b92092204fe480 HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Connection: keep-alive授权成功返回如下参数:
- code:表示授权码,必选项。通常授权码只能使用该码一次,并且会设置有效时间。
- state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
3、客户端使用授权码向认证服务器申请令牌。
这个过程发生在客户端后台,用户不可见,这也是授权码模式的特性,更安全。大致接口地址类似(服务端会验证client_id、client_secret、auth code的正确性或更新令牌 refresh_token):
http://localhost:8080/oauth2/access_token?client_id={AppKey}&client_secret={AppSecret}&grant_type=authorization_code&redirect_uri={YourSiteUrl}&code={code}
向认证服务器申请令牌包含以下参数:
- grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
- code:表示上一步获得的授权码,必选项。
- redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
- client_id:表示客户端ID,必选项
- client_secret:表示客户端密钥,必选项。
4、如果授权码验证成功,则下生成一个令牌。
这个过程发生在服务端后台,服务端会验证client_id、client_secret、auth code的合法性,成功则生成一个令牌。
认证服务器返回,包含以下参数:
- access_token:表示访问令牌,必选项。
- token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
- expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
- refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
- scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
5、客户端使用访问令牌向资源服务器请求受保护资源。
客户端实现,请求用户保护的资源。
知乎成功后返回:
GET /oauth/account_callback?callbacktype=sina HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Connection: keep-alive
6、资源服务器验证访问令牌的有效性则开放保护的资源。
服务端实现,验证令牌合法性后则开放用户授权的保护资源,很多时候,用户的资源是与权限对应的。令牌可能只包含了用户特定的授权凭据,准确的说,令牌对应用户授权时所赋予的一系列权限的集合。所以在这一步,除了校验令牌的合法性之外,服务端还需对该令牌是否拥有足够的权限执行被保护操作进行验证。
一些问题
简单例举我想到的一些问题吧
1.请求授权码过程服务端最好启用授权域(scope)需要对授权的令牌的有效权限进行验证,以免泄露过多用户数据 ,启用state,用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验。
2.获得授权码重定向的地址,为了防止有攻击者伪造重定向地址骗取用户授权,服务提供方应对授权时的重定向地址进行验证或判断是不是同一个域,所以申请应用的时候应该有appkey,appsecret,redirect_uri基础配置参数。
3.确保授权码一次性与有效时间,令牌有效时间验证,改用HTTPS来加密,确保通信内容不被第三方窃取。
Refer:
Docs » OAuth 2.0
http://oauthlib.readthedocs.org/en/latest/oauth2/oauth2.html
理解OAuth 2.0
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
OAuth2授权原理
http://www.cnblogs.com/neutra/archive/2012/07/26/2609300.html
OAuth 2.0模式
http://www.dannysite.com/blog/?tag=204
OAuth 2.0安全案例回顾
http://drops.wooyun.org/papers/598