什么是OAuth
如今很多网站的功能都强调彼此间的交互,因此我们需要一种简单,标准的解决方案来安全的完成应用的授权,于是,OAuth应运而生,看看官网对其的定义:
An open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.
一个典型的OAuth应用通常包括三种角色,分别是:
- Consumer:消费方
- Service Provider:服务提供者
- User:用户
用户好理解,不必多言,消费方和服务提供者则需要解释一下,举例来说:假设我们做了一个SNS,它有一个功能,可以让会员把他们在Google上的联系人导入到SNS上,那么此时的消费方就是SNS,而服务提供者则是Google。
注:Google APIs支持OAuth。
消费方如果想使用服务提供者的OAuth功能,通常需要先申请两样东西:
- Consumer Key
- Consumer Secret
当消费方生成签名的时候,会用到它们。
一个典型的OAuth流程通常如下图所示:
- A:消费方请求Request Token
- B:服务提供者授权Request Token
- C:消费方定向用户到服务提供者
- D:获得用户授权后,服务提供者定向用户到消费方
- E:消费方请求Access Token
- F:服务提供者授权Access Token
- G:消费方访问受保护的资源
基本就是用Request Token换取Access Token的过程。这里需要注意的是,对服务提供者而言,Request Token和Access Token的生命周期不一样,通常,Request Token的生命周期很短,一般在一个小时以内,这样相对安全一些;而Access Token的生命周期很长,往往是无限,如此一来,消费方就可以把它保存起来,以后的操作就无需用户再授权了,即便用户修改账号密码,也不会受影响,当然,用户可以废除消费方的授权。
有腿的OAuth
我们前面描述的OAuth,被称为三条腿的OAuth(3-Legged OAuth),这也是OAuth的标准版本。这里所谓的“三条腿”,指的是授权过程中涉及前面提到的三种角色,也就是:消费方,服务提供者,用户。不过有些情况下,不需要用户的参与,此时就产生了一个变体,被称作两条腿的OAuth(2-Legged OAuth),两条腿的OAuth和三条腿的OAuth相比,因为没有用户的参与,所以在流程中就不会涉及用户授权的环节,而主要是通过Consumer Key和Consumer Secret来完成签名的,此时的Consumer Key和Consumer Secret基本等价于账号和密码的作用。
OAuth简史
2007年12月4日发布了OAuth Core 1.0:
此版本的协议存在严重的安全漏洞:OAuth Security Advisory: 2009.1,更详细的介绍可以参考:Explaining the OAuth Session Fixation Attack。
2009年6月24日发布了OAuth Core 1.0 Revision A:
此版本的协议修复了前一版本的安全漏洞,并成为RFC5849,我们现在使用的OAuth版本多半都是以此版本为基础。
OAuth的未来:OAuth 2.0 Working Draft,…
OAuth四种授权流程
下面我们具体介绍一下OAuth2.0提供的四种授权流程:
Authorization Code
可用范围:此类型可用于有服务端的应用,是最贴近老版本的方式。
+----------+ | 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)
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = code client_id redirect_uri scope state
Authorization Server在Resource Owner授权后给Client返回Authorization Code:
code state
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = authorization_code code client_id client_secret redirect_uri
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token token_type expires_in refresh_token
说明:基本流程就是拿Authorization Code换Access Token。
Implicit Grant
可用范围:此类型可用于没有服务端的应用,比如Javascript应用。
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+
Client向Authorization Server发出申请(/oauth/2.0/authorize):
response_type = token client_id redirect_uri scope state
Authorization Server在Resource Owner授权后给Client返回Access Token:
access_token token_type expires_in scope state
说明:没有服务端的应用,其信息只能保存在客户端,如果使用Authorization Code授权方式的话,无法保证client_secret的安全。BTW:不返回Refresh Token。
Resource Owner Password Credentials
可用范围:不管有无服务端,此类型都可用。
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
Clien向Authorization Server发出申请(/oauth/2.0/token):
grant_type = password username password scope
AuthorizationServer给Client返回AccessToken:
access_token token_type expires_in refresh_token
说明:因为涉及用户名和密码,所以此授权类型仅适用于可信赖的应用。
Client Credentials
可用范围:不管有无服务端,此类型都可用。
+---------+ +---------------+ | | | | | |>--(A)- Client Authentication --->| Authorization | | Client | | Server | | |<--(B)---- Access Token ---------<| | | | | | +---------+ +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = client_credentials client_id client_secret scope
Authorization Server给Client返回Access Token:
access_token token_type expires_in
说明:此授权类型仅适用于获取与用户无关的公共信息。BTW:不返回Refresh Token。
…
流程中涉及两种Token,分别是Access Token和Refresh Token。通常,Access Token的有效期比较短,而Refresh Token的有效期比较长,如此一来,当Access Token失效的时候,就需要用Refresh Token刷新出有效的Access Token:
+--------+ +---------------+ | |--(A)------- Authorization Grant ------->| | | | | | | |<-(B)----------- Access Token -----------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token --------->| | | | | | | |<-(H)----------- Access Token -----------| | +--------+ & Optional Refresh Token +---------------+
Client向Authorization Server发出申请(/oauth/2.0/token):
grant_type = refresh_token refresh_token client_id client_secret scope
Authorization Server给Client返回Access Token:
access_token expires_in refresh_token scope
OAuth和OpenID的区别
OAuth关注的是authorization;而OpenID侧重的是authentication。从表面上看,这两个英文单词很容易混淆,但实际上,它们的含义有本质的区别:
- authorization: n. 授权,认可;批准,委任
- authentication: n. 证明;鉴定;证实
OAuth关注的是授权,即:“用户能做什么”;而OpenID关注的是证明,即:“用户是谁”。
如果混淆了OAuth和OpenID的含义,后果很严重。以国内某网站开发的应用为例:它的功能是通过OAuth授权让新浪微博和豆瓣的用户使用各自的身份发表评论,如下图所示:
此类应用属于身份证明问题,本应该通过OpenID来实现,但因为错误的使用了OAuth,从而带来安全隐患:设想一下用户只是在网站上发表了评论而已,但却赋予了网站随意操作自己私有数据的权利!这就好比:快递员送包裹,为了证明收件人的身份,原本你只要给他看一下身份证即可,可你却把防盗门钥匙都给他了!Oh,My God!
BTW:关于OAuth详细的介绍可以参考http://tools.ietf.org/html/draft-ietf-oauth-v2-31。