zoukankan      html  css  js  c++  java
  • OAuth的机制原理讲解及开发流程

    一. OAuth 2.0 协议

    OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的。OAuth 2.0 是目前比较流行的做法,它率先被Google, Yahoo, Microsoft, Facebook等使用。之所以标注为 2.0,是因为最初有一个1.0协议,但这个1.0协议被弄得太复杂,易用性差,所以没有得到普及。2.0是一个新的设计,协议简单清晰,但它并不兼容1.0,可以说与1.0没什么关系。所以,我就只介绍2.0。

    1.1 协议的参与者
    从引言部分的描述我们可以看出,OAuth的参与实体至少有如下三个:

    · RO (resource owner): 资源所有者,对资源具有授权能力的人。如上文中的用户Alice。

    · RS (resource server): 资源服务器,它存储资源,并处理对资源的访问请求。如Google资源服务器,它所保管的资源就是用户Alice的照片。

    · Client: 第三方应用,它获得RO(resource owner)的授权后便可以去访问RO(resource owner)的资源。如网易印像服务。

    此外,为了支持开放授权功能以及更好地描述开放授权协议,OAuth引入了第四个参与实体:

    · AS (authorization server): 授权服务器,它认证RO的身份,为RO(resource owner)提供授权审批流程,并最终颁发授权令牌(Access Token)。读者请注意,为了便于协议的描述,这里只是在逻辑上把AS(authorization server)与RS(resource server)区分开来;在物理上,AS与RS(resource server)的功能可以由同一个服务器来提供服务。

    1.2 OAuth的原理

    如图1所示,协议的基本流程如下:

    (1) Client请求RO的授权,请求中一般包含:要访问的资源路径,操作类型,Client的身份等信息。

    (2) RO批准授权,并将“授权证据”发送给Client。至于RO如何批准,这个是协议之外的事情。典型的做法是,AS提供授权审批界面,让RO显式批准。这个可以参考下一节实例化分析中的描述。

    (3) Client向AS请求“访问令牌(Access Token)”。此时,Client需向AS提供RO的“授权证据”,以及Client自己身份的凭证。

    (4) AS验证通过后,向Client返回“访问令牌”。访问令牌也有多种类型,若为bearer类型,那么谁持有访问令牌,谁就能访问资源。

    (5) Client携带“访问令牌”访问RS上的资源。在令牌的有效期内,Client可以多次携带令牌去访问资源。

    (6) RS验证令牌的有效性,比如是否伪造、是否越权、是否过期,验证通过后,才能提供服务。

    1.3 OAuth2.0新特性

    • 服务器角色区分:授权服务器和资源服务器
    • 区别不同的用户类型、授权场景和授权流程
    • 将token分为频繁传输使用但是有效时长较短的Access Token和用于更新Access Token的Refresh Token
    • 定义多种token,降低资源请求的构造难度
    • 授权过程不签名,可根据需要采用HTTPS加密传输、验证客户端密钥(通过签名)、客户端注册时预先指定callback地址等手段。
    • 明确引入客户端注册流程:确定Client Type、callback URL以及其它信息
    • 引入可选的state参数,帮助客户端防范CSRF和管理状态
    • 引入scope参数,可以进行授权范围控制
    • token type等可被扩展: bearer(HTTPS), mac(HTTP+sign), etc.

    1.4 授权类型
      在开放授权中,第三方应用(Client)可能是一个Web站点,也可能是在浏览器中运行的一段JavaScript代码,还可能是安装在本地的一个应用程序。这些第三方应用都有各自的安全特性。对于Web站点来说,它与RO(resource owner)浏览器是分离的,它可以自己保存协议中的敏感数据,这些密钥可以不暴露给RO(resource owner);对于JavaScript代码和本地安全的应用程序来说,它本来就运行在RO(resource owner)的浏览器中,RO(resource owner)是可以访问到Client在协议中的敏感数据。

    OAuth为了支持这些不同类型的第三方应用,提出了多种授权类型,如

    1. 授权码 (Authorization Code Grant)
    2. 隐式授权 (Implicit Grant)
    3. RO(resource owner)凭证授权 (Resource Owner Password Credentials Grant)
    4. Client凭证授权 (Client Credentials Grant)

    由于本文旨在帮助用户理解OAuth协议,所以我将先介绍这些授权类型的基本思路,然后选择其中最核心、最难理解、也是最广泛使用的一种授权类型——“授权码”,进行深入的介绍。

    二 几类授权分析

    2.1  授权码类型的开放授权的流图

    如上图所示,授权码类型的开放授权协议流程描述如下:

    (1) Client初始化协议的执行流程。首先通过HTTP 302来重定向RO用户代理到AS。Client在redirect_uri中应包含如下参数:client_id, scope (描述被访问的资源), redirect_uri (即Client的URI), state (用于抵制CSRF攻击). 此外,请求中还可以包含access_type和approval_prompt参数。当approval_prompt=force时,AS将提供交互页面,要求RO必须显式地批准(或拒绝)Client的此次请求。如果没有approval_prompt参数,则默认为RO批准此次请求。当access_type=offline时,AS将在颁发access_token时,同时还会颁发一个refresh_token。因为access_token的有效期较短(如3600秒),为了优化协议执行流程,offline方式将允许Client直接持refresh_token来换取一个新的access_token。

    (2) AS认证RO身份,并提供页面供RO决定是否批准或拒绝Client的此次请求(当approval_prompt=force时)。

    (3) 若请求被批准,AS使用步骤(1)中Client提供的redirect_uri重定向RO用户代理到Client。redirect_uri须包含authorization_code,以及步骤1中Client提供的state。若请求被拒绝,AS将通过redirect_uri返回相应的错误信息。

    (4) Client拿authorization_code去访问AS以交换所需的access_token。Client请求信息中应包含用于认证Client身份所需的认证数据,以及上一步请求authorization_code时所用的redirect_uri。

    (5) AS在收到authorization_code时需要验证Client的身份,并验证收到的redirect_uri与第3步请求authorization_code时所使用的redirect_uri相匹配。如果验证通过,AS将返回access_token,以及refresh_token(若access_type=offline)。

    ================================暂时保留==========================================

     OAuth2.0的认证流程

            在OAuth2.0的处理流程,主要分为以下四个步骤:

            1)得到授权码code

            2)获取access token

            3)通过access token,获取OpenID

            4)通过access token及OpenID调用API,获取用户授权信息

            上面是流程的大概四个步骤,在下面的流程示意图中会得到体现,这是我制作的一个幻灯片的流程图(文章最后会附上制作的OAuth幻灯片分享给大家),这里就直接截图下来进行讲解:

            第一步:首先直接跳转至用户授权地址,即图示 Request User Url ,提示用户进行登录,并给予相关资源授权,得到唯一的Auth code,这里注意的是code只有10分钟的有效期,对于安全考虑,相对于OAuth 1.0省了一步获取临时的Token,并且有效期也进行了控制,比1.0认证简化了很多,并安全一些;

            第二步:得到授权code后,这一步就是请求access token,通过 图示 Request access url ,生成得到数据Token;

            第三步:通过Access Token请求OpenID,OpenID是用户在此平台的唯一标识,通过图示 Request info url 请求,然后得到OpenID;

            第四步:通过第二步得到的数据Token、第三步得到的OpenID及相关API,进行请求,获取用户授权资源信息。(我喜欢简洁明了,OAuth2.0认证流程就是这样,相信通过图示及讲解都能明白,若有不明白之处请留言)

            最后,分享自己关于OAuth1.0、2.0认证流程讲解的幻灯片,结合文章来看,你会更容易理解。

            课件分享:点击下载 (麻烦保留博主信息,谢谢)

    ================================暂时保留==========================================

    流程分解
    1. 授权场景

    Authorization code 授权适用于PC,无线客户端等需要和第三方server进行交互的应用场景。使用Authorization code授权,第三方能够集中处理用户的授权请求和授权结果,适用于有server端的应用。

    2. 授权流程

    Authorization code授权模式分为两步,首先获取authorization code,然后用code获取acces token。

    示意图:

    Authorization Code Grant

    交互图:

    Authorization Code Grant

    3. 过程详解

    1、获取Authorization Code

    请求参数

    • client_id 必须 分配给应用的appid
    • redirect_uri 必须 授权回调地址,必须和应用注册的地址一致
    • response_type 必须授权类型,此值固定为“code”
    • state 必须 client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。
    • scope 可选 授予权限范围
    • 其他参数

    如果用户成功授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值

    2、通过Authorization Code获取Access Token

    请求参数

    • client_id 必须 分配给应用的appid
    • grant_type 必须 授权类型,此值为:authorization_code
    • client_secret 必须 分配给应用的secret
    • state 必须 client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。
    • redirect_uri 必须 与上面一步中传入的redirect_uri保持一致
    • code 必须 上一步返回的Authorization Code值(必须设定此code的有效时间)

    返回参数

    • access_token 必须 授权令牌
    • expires_in 必须 该access_token的有效期
    • refresh_token 可选 在授权自动续期步骤中,获取新的Access_Token时需要提供的参数
    • 其他参数 可选

    3、[可选] 权限自动续期,获取access_token

    Access_token一般需要根据应用特性设定有效期,过期后需要用户重新授权或采用自动续期的方式。

    请求参数

    • grant_type 必须 授权类型,在本步骤中,此值为“refresh_token”
    • client_id 必须 分配给应用的appid
    • refresh_token 必须 第二步返回的refresh_token
    • client_secret 必须 分配给应用的secret

    如果授权成功,则会返回和步骤二同样的结果。

    2.2 Implicit 授权

    1. 授权场景

    Implicit授权一般适用于没有server端的客户端应用,由客户端发起授权请求,保存和处理access_token,但有些应用(如web应用等)为了提高用户体验,简化授权过程,也会常采用Implicit授权方式(注意,这种授权方式没有返回refresh_token。)

    2. 授权流程

    示意图:

    Implicit Grant

    交互图:

    Implicit Grant Flow

    交互图:

    3. 过程详解

    请求参数

    • response_type 必须 授权类型,在本步骤中,此值为“token”
    • client_id 必须 分配给应用的appid
    • redirect_uri 必须 授权回调地址,必须和应用注册的地址一致
    • scope 可选 授予权限范围
    • state 必须 client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。
    • 其他参数 可选

    如果成功授权,则会跳转到redirect_uri指定的回调地址,并带上access_token、expires_in、state以及与应用相关的参数。注意,这种授权方式没有返回refresh_token。

    2.3 Resource Owner Password Credentials

    该授权方式获取access token一般只有一步,类似如下GET/POST请求:https://open.xxx.com/oauth2/access_token?client_id=xxxx&client_secret=xxxxx&grant_type=password&username=xxx&password=xxx

    三、案例

    3.1 淘宝的OAuth2.0安全控制

    • 只支持HTTPS(bears),不支持签名(签名是走以前老的授权方式)
    • 授权过程指定的callback URL必须与注册的callback URL在同一个域名,或者为um:ietf:wg:oauth:2.0:oob(表示只显示授权码,不回调callback URL)
    • 接口分不同级别(R1, R2, W1, W2),同一Token对不同级别接口有不同的有效期
    • 接口权限划分(item, promotion, user, etc.)

    3.2 google的授权示例

    下面我以实例化方式来帮助读者理解授权码类型的授权协议的运行过程。假设: 
    (1) Alice有一个有效的Google帐号;
    (2) Facebook.com已经在Google Authorization Server上注册了Client身份,已经获得(client_id, client_secret),注意client_secret是Client与AS之间的一个共享密钥。
    (3) Alice想授权Facebook.com查看她的联系人列表(https://www.google.com/m8/feeds)。

    图3展示了Alice、Facebook.com、Google资源服务器、以及Google OAuth授权服务器之间的协议运行过程。


          图3

    协议所涉及到的细节都已经在图3上了,所以不打算再做详细介绍了。若看懂了此图,OAuth2.0就理解了。

    读者请注意,在步骤(4)中,Client需要拿“授权码”去换“授权令牌”时,Client需要向AS证明自己的身份,即证明自己就是步骤(2)中Alice批准授权时的Grantee。这个身份证明的方法主要有两种(图3中使用了第1种):
    (1) 通过https直接将client_secret发送给AS,因为client_secret是由Client与AS所共享,所以只要传送client_secret的信道安全即可。
    (2) 通过消息认证码来认证Client身份,典型的算法有HMAC-SHA1。在这种方式下,Client无需传送client_secret,只需发送消息请求的signature即可。由于不需要向AS传递敏感数据,所以它只需要使用http即可。

    此外, 在步骤(2)中,Google授权服务器需要认证Alice的RO身份,并提供授权界面给Alice进行授权审批。今天Google提供的实例如图4、图5所示,仅供读者理解OAuth这种“现场授权”或"在线授权"的含义。

    四 OAuth设计上的安全性考虑

    4.1 为何引入authorization_code?

    协议设计中,为什么要使用authorization_code来交换access_token?这是读者容易想到的一个问题。也就是说,在协议的第3步,为什么不直接将access_token通过重定向方式返回给Client呢?比如:

    HTTP/1.1 302
    Location:
    https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600

    如果直接返回access_token,协议将变得更加简洁,而且少一次Client与AS(authorization server)之间的交互,性能也更优。那为何不这么设计呢?协议文档[1]中并没有给出这样设计的理由,但也不难分析:

    (1) 浏览器的redirect_uri是一个不安全信道,此方式不适合于传递敏感数据(如access_token)。因为uri可能通过HTTP referrer被传递给其它恶意站点,也可能存在于浏览器cacher或log文件中,这就给攻击者盗取access_token带来了很多机会。另外,此协议也不应该假设RO(resource owner)用户代理的行为是可信赖的,因为RO(resource owner)的浏览器可能早已被攻击者植入了跨站脚本用来监听access_token。因此,access_token通过RO(resource owner)的用户代理传递给Client,会显著扩大access_token被泄露的风险。 但authorization_code可以通过redirect_uri方式来传递,是因为authorization_code并不像access_token一样敏感。即使authorization_code被泄露,攻击者也无法直接拿到access_token,因为拿authorization_code去交换access_token是需要验证Client的真实身份。也就是说,除了Client之外,其他人拿authorization_code是没有用的。 此外,access_token应该只颁发给Client使用,其他任何主体(包括RO(resource owner))都不应该获取access_token。协议的设计应能保证Client是唯一有能力获取access_token的主体。引入authorization_code之后,便可以保证Client是access_token的唯一持有人。当然,Client也是唯一的有义务需要保护access_token不被泄露。

    (2) 引入authorization_code还会带来如下的好处。由于协议需要验证Client的身份,如果不引入authorization_code,这个Client的身份认证只能通过第1步的redirect_uri来传递。同样由于redirect_uri是一个不安全信道,这就额外要求Client必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。引入authorization_code之后,AS(authorization server)可以直接对Client进行身份认证(见步骤4和5),而且可以支持任意的Client认证方式(比如,简单地直接将Client端密钥发送给AS)。

      在我们理解了上述安全性考虑之后,读者也许会有豁然开朗的感觉,懂得了引入authorization_code的妙处。那么,是不是一定要引入authorization_code才能解决这些安全问题呢?当然不是。笔者将会在另一篇博文给出一个直接返回access_token的扩展授权类型解决方案,它在满足相同安全性的条件下,使协议更简洁,交互次数更少。

    4.2 基于Web安全的考虑

      OAuth协议设计不同于简单的网络安全协议的设计,因为OAuth需要考虑各种Web攻击,比如CSRF (Cross-Site Request Forgery), XSS (Cross Site Script), Clickjacking。要理解这些攻击原理,读者需要对浏览器安全(eg, Same Origin Policy, 同源策略)有基本理解。比如,在redirect_uri中引入state参数就是从浏览器安全角度考虑的,有了它就可以抵制CSRF攻击。如果没有这个参数,攻击者便可以在redirect_uri中注入攻击者提供的authorization_code或access_token,结果可能导致Client访问错误的资源(比如,将款项汇到一个错误的帐号)。

      基于Web安全的考虑,OAuth协议文档中已经有了比较全面的阐述,所以我不打算在此文中进行展开,有兴趣的读者请参考[1]。

  • 相关阅读:
    Gridview linkbutton click
    Linux Bluetooth编程 HCI层编程
    Google Android 开发工程师职位面试题
    Android下文件操作模式(含SDCard的读写)
    Android中悬浮窗口的实现原理和示例代码
    Android.mk的用法和基础
    Android.mk的用法和基础
    Linux Bluetooth编程 HCI层编程
    Android下文件操作模式(含SDCard的读写)
    init.rc 脚本语法学习
  • 原文地址:https://www.cnblogs.com/duanxz/p/4022679.html
Copyright © 2011-2022 走看看