zoukankan      html  css  js  c++  java
  • 资源授权?对OAuth2.0的一次重新认识的过程

    什么是OAuth?

    OAuth一个开放的授权标准允许用户在不提供关键信息(如账号,密码)给第三方应用的前提下,让第三方应用去访问用户在某网站上的资源(如头像,用户昵称等)

    OAuth分为OAuth1.0和OAuth2.0两个版本,后来随着OAuth2.0被使用的越来越广泛,OAuth1.0逐渐退出舞台(当然仍有少部分系统在使用1.0授权标准)。下面我们围绕OAuth2进行展开。

    OAuth 2.0致力于简化客户端开发人员的工作,同时为Web应用程序,桌面应用程序,移动电话和客厅设备提供特定的授权流程。

    我们以博客园登录为例:

    新同学小明想在“博客园”发表文章,这时候他进入登录页面,“博客园”登录首页需要小明提供用户名和密码才允许小明登入。

     而由于小明是第一次使用“博客园”,他对博客园可能不是很信任,于是他想通过下方的他信任QQ应用直接登录。

    来到这里我们发现,通过授权,“博客园”将获得小明在QQ应用中的资源(昵称,头像,性别),并成功登录“博客园”,而并不需要提供用户名和密码给“博客园”。

    上例中“博客园”就是所谓的第三方应用,而QQ就是提供受保护资源的某个应用。

    OAuth2四个参与角色

    1.资源所有者(Resource Owner):资源的拥有者(上例中:小明)
    2.资源服务器(Resource Server):资源所在的服务器(上例中:QQ应用)
    3.授权服务器(Authorization Server):用于验证client(第三方应用)的真实性,提供授权码和令牌token。授权服务器可单独部署,也可以和资源服务器一起部署。
    4.第三方应用(Client):访问受保护资源的客户端,上例中:博客园

    这里,很多同学会对“授权服务器”存在疑惑,上例中博客园登录流程并未体现授权服务器啊?我们接着往下看

    OAuth2标准授权流程

    结合OAuth2经典流程图,我们一下再来看下小明登录“博客园”的流程。

    (A)第三方应用(client)“博客园”向(资源拥有者)小明发起授权请求,即小明点击博客园登录页中的QQ登录。
    
    (B)QQ登录授权页面,小明登录自己的QQ账号,同意授权给“博客园”,并返回授权许可凭证。
    
    (C)第三方应用(client)“博客园”拿着步骤(B)获取的授权许可凭证,向授权服务器发起请求。
    
    (D)授权服务器同意第三方应用(client)“博客园”的请求,并返回一个令牌Token。
    
    (E)第三方应用(client)“博客园”拿着Token请求(资源服务器)QQ应用中的昵称,头像等资源
    
    (F)(资源服务器)QQ应用验证Token通过,并返回资源给第三方应用(client)“博客园”

    通过上述流程,我们可以看出,当QQ授权同意后,并不是马上就将QQ应用中的资源返回给第三方客户端的,而是需要客户端拿着授权许可凭证,向授权服务器请求并获取最终的钥匙Token,然后才能获得受保护资源。

    显然,授权服务器充当了一个验证client,并颁发token令牌的服务角色。

    那么授权许可凭证到底是什么呢?

    OAuth2授权许可凭证

    1.授权码(Authorization Code)

     该模式目前是功能最完整、流程最严密的授权模式。一般需要client有专门的后端server,根据获取的授权码code在后端server请求令牌token。

    上例中博客园登录授权就是用的“授权码模式”,交互流程如下:

    1)博客园向小明发起QQ授权请求。
    
    2)QQ授权验证同意后,根据博客园提供的Redirect_url返回,并带上授权码code
    
    3)博客园前端根据返回的url获取授权码code,并在后端server向授权服务器发起请求获取token
    
    4)授权服务验证通过,并以json格式返回token
    
    5)博客园再根据token请求QQ应用中获取受保护资源(头像,昵称等)

       步骤(1),授权请求(Authorization Request):

       client需提供如下主要参数:

       1.client_id:必填,第三方应用唯一标识ID
    
       2.redirect_uri:必填,授权同意后,重定向地址URL
    
       3.response_type:必填,授权码模式下固定值为“code”
    
       4.state:选填,一个状态码,可用于防止跨站请求伪造(CSRF)攻击。客户端发起授权请求时会生成一个状态码与客户端绑定,授权请求成功后会将该state原样返回
    
       5.scope:选填,标识授权范围

       例如博客园向QQ发起授权请求:

    https://graph.qq.com/oauth2.0/show?which=Login&display=pc
    &client_id=101880508
    &scope=get_user_info
    &response_type=code
    &redirect_uri=***
    &state=*****

        扩展:跨站请求伪造(CSRF)攻击:用户登录了A可信网站,认证信息保存在浏览器cookie中。当用户访问攻击者创建的B网站时,用户认证信息仍有效,攻击者通过在B网站发送一个伪造的请求提交到A网站服务器上,让A网站服务器误以为请求来自于自己的网站。  

     步骤(2),授权请求同意后,返回信息:

      1.code:授权码
    
      2.state:原样返回,client提交的state状态码

      步骤(3),根据授权码code,后端请求token,client需提供如下参数

    1.grant_type:必填。授权码模式,固定值“authorization_code”。
    
    2.code : 必填。授权同意后返回的授权码。
    
    3.redirect_uri:必填。授权同意后,重定向地址URL

    4.client_id:必填。第三方应用唯一标识ID。

    5.client_secret:必填。第三方应用授权申请的秘钥。

     例如:

    POST /oauth/token HTTP/1.1
    Host: authorization-server.com
     
    grant_type=authorization_code
    &code=xxxxxxxxxxx
    &redirect_uri=https://example-app.com/redirect
    &client_id=xxxxxxxxxx
    &client_secret=xxxxxxxxxx

    步骤(4),授权认证通过,返回主要参数。

    1.access_token:访问令牌。
    
    2.refresh_token:刷新令牌。
    
    3.expires_in:令牌过期时间。

    4.token_type:令牌类型。

    其中刷新令牌refresh_token的作用是,当访问令牌token失效时,无需重新发起授权获取新的token,根据刷新令牌直接请求授权服务,就可以获取新的令牌。

    2.隐式许可(Implicit)

     授权码模式的简化应用,跳过了获取授权码code的过程,直接获取token。一般用于没有后端的Client。

     如果博客园使用该模式,其工作流程:

    1)博客园向小明发起QQ授权请求。
    
    2)QQ授权验证同意后,根据博客园提供的Redirect_url返回,并带上令牌token

    3)博客园再根据token请求QQ应用中获取受保护资源(头像,昵称等)

      步骤(1),授权请求参数和授权码模式参数一样,唯一不同的参数是response_type,Implicit模式下固定值为“token”。

      步骤(2),授权请求同意后,返回信息:

    1.access_token:访问令牌。

    2.token_type:令牌类型。

    3.expires_in:令牌过期时间。

      注:隐式授权模式颁发的令牌,不提供refresh刷新令牌

    URL格式:

    格式:https://a.com/callback#token=ACCESS_TOKEN

    注意,令牌的位置是 URL 锚点“#”后面,而不是查询字符串“?”后面,这是因为 OAuth2允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。

    3.用户密码模式(Resource Owner Password Credentials)

     客户端提供用户名和密码,获取token。前面我们说OAuth2就是为了避免直接提供用户名和密码给第三方应用程序而诞生,那么这里又是怎么回事呢?

     其实,该授权模式的初衷是为服务自己的应用启用密码登录,用户使用其用户名和密码登录该服务的网站或本机应用程序,但是绝对不允许第三方应用程序询问用户密码。

     授权请求,提供参数:

    1.grant_type:必填。该模式下固定值为“password”。
    
    2.username:必填。用户登陆名。
    
    3.passward:必填。用户登陆密码。
    
    4.scope:可填。表示授权范围。

    5.客户端认证参数:通常如果授权服务管理系统给客户端颁发了身份秘钥信息(client_id,client_secret),那么客户端发起授权请求时需要携带参数client_id和client_secret。或在HTTP Basic auth标头中接受客户端client_secret和密码client_secret

       其中,客户端认证参数:client_id和client_secret主要是用于授权服务验证客户端的身份。如果客户端都没在授权服务管理系统备案(不需要验证客户端身份),那么授权请求就不需要这两个参数。备案又是什么呢?大家请留意文章末尾。

       以postman请求token为例:

       第一种方式:将client_id和client_secret作为请求体参数

     第二种方式:在HTTP Basic auth请求头中单独验证client_secret和client_secret

     

    授权请求同意后,返回主要参数:

    1.access_token:访问令牌。
    
    2.token_type:令牌类型。
    
    3.expires_in:令牌过期时间。
    
    4.scope:授权范围

    4.客户端模式(Client Credentials)

     当第三方应用程序请求访问令牌以访问其自己的资源(而非代表其他用户去访问资源)时,使用该模式。此时第三方应用程序将自己当成资源所有者,直接请求授权服务器获取令牌token。

     授权请求,提供参数:

    1.grant_type:必填。该模式下固定值为“client_credentials”。
    
    2.scope:可填。表示授权范围。

    3.客户端认证:必填,包含参数client_id和client_secret;或在HTTP Basic auth标头中接受客户端
    client_secret和密码client_secret

    以postman请求token为例:

    第一种方式:将client_id和client_secret作为请求体参数

     第二种方式:在HTTP Basic auth请求头中单独验证client_secret和client_secret

     

    两种方式效果一致,都是通过client_id和client_secret,让授权服务器验证客户端的身份。

    授权请求同意后,返回参数:

    1.access_token:访问令牌。
    
    2.token_type:令牌类型。
    
    3.expires_in:令牌过期时间。
    
    4.scope:授权范围

    扩展:第三方应用发起授权请求时,client_id和client_secret是哪来的呢?

    授权服务提供第三方应用的管理:

    第三方应用申请令牌之前,都必须先到授权服务系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

    5.刷新令牌refresh  token

    前面我们说,当授权请求同意后,通常授权服务器会返回一个刷新令牌refresh  token给我们(该返回参数非必选的,由授权服务定制,通常推荐返回该参数)。

    当访问令牌access token 过期后,如果重新发起一遍请求令牌的过程显然有点麻烦,这时候通过refresh token发起一次请求可以直接获取新的访问令牌access token。

    请求参数:

    1.grant_type:必填。固定值为“refresh_token”。
    
    2.refresh_token:必填。
    
    3.scope:可填。表示授权范围。
    
    4.客户端认证:通常如果授权服务管理系统给客户端颁发了身份秘钥信息(client_id,client_secret),那么客户端发起授权请求时需要携带参数client_id和client_secret。或在HTTP Basic auth标头中接受客户端client_secret和密码client_secret。
    如果客户端不需要身份认证,则无需携带任何身份认证的信息

    例如:

    POST /oauth/token HTTP/1.1
    Host: authorization-server.com
     
    grant_type=refresh_token
    &refresh_token=xxxxxxxxxxx
    &client_id=xxxxxxxxxx
    &client_secret=xxxxxxxxxx

    本文,我们结合博客园授权QQ登录的案例,描述了OAuth2的基本概念,OAuth2的授权流程以及各种授权模式的使用。多动手,多动手,多动手才能加深自己的理解。

    附:推荐几篇值得学习的OAuth2文章

    1.OAuth2.0协议标准

    2.大神阮一峰的文章:OAuth2.0的四种方式

  • 相关阅读:
    版本管理系统:svn和git
    Java学习笔记七 常用API对象三
    Java学习笔记六 常用API对象二
    Java学习笔记五 常用API对象一
    Java学习笔记三.3
    Java学习笔记三.2
    Java学习笔记三
    析构函数总结
    C++之类的构造函数,不得不学明白的重点
    C++拷贝构造函数(深拷贝,浅拷贝)
  • 原文地址:https://www.cnblogs.com/chenxf1117/p/14416365.html
Copyright © 2011-2022 走看看