美文网首页identity server
ID03_OAuth2.0平铺直叙

ID03_OAuth2.0平铺直叙

作者: wanzhouyi | 来源:发表于2018-05-09 00:18 被阅读0次

    先说一说OAuth2.0中的几个角色,它是OAuth理论的基石:

    • 客户端(Client),即第三方应用,它在认证流程中作为流程发起者向认证服务器发起认证请求以获得用户账户授权。流程发起前需要经过用户同意。
    • 资源服务器(Resouce Server),用于访问用户信息的服务器。
    • 授权服务器(Authorization Server),在验证资源所有者之后发放令牌给客户端。
    • 资源拥有者(User),用户

    接下来就可以开始创建应用了。为了能启动认证流程,开发者必须对应用进行注册,通常来说注册一个应用就是填入logo、应用名、站点名等信息。如果是网站应用,则会要求填入一个重定向URI。

    • 关于重定向URI。该授权服务器只会将用户重定向到注册的URI,这有助于防止一些攻击。任何重定向URI都必须使用TLS进行安全防护,因此该服务只会重定向到以“https”开头的URI。这可以防止在授权过程中拦截令牌。本地应用程序可能会为应用程序注册一个带有自定义URL方案的重定向URI,可能如下所示demoapp://redirect
    • 客户端ID(Client ID)和密钥(Client Secret)。当应用注册成功后,会生成客户端ID和密钥。客户端ID是公开信息,用于构建登录链接,或者是包含在js源码中。而客户端的密钥则必须保持其私密性。

    应用注册成功后就可以发起认证流程了。
    OAuth2.0认证流程的第一步是获得用户的授权,对于网页或移动应用这个过程通常是向用户展示一个页面征询其是否同意授权。在上一篇文章中已经讲过,OAuth2.0主要提供了四种授权类型,即授权码模式、密码模式、客户端凭证以及简单(隐式)模式。下面将详细描述这几种模式。

    网站应用

    首先应获得用户授权

    为此我们创建一个认证链接,如:

    https://authorization-server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
    
    • code - 表示服务器期望收到"授权码"
    • client_id - 创建应用程序时收到的客户端ID
    • redirect_uri - 指示在授权完成后将用户重定向的URI
    • scope- 一个或多个范围值,指示希望访问用户帐户的哪些部分
    • state- 由应用程序生成的随机字符串,稍后将对其进行验证
      以使用QQ账户登录简书为例
    https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=100410602&redirect_uri=http%3A%2F%2Fwww.jianshu.com%2Fusers%2Fauth%2Fqq_connect%2Fcallback&response_type=code
    

    呈现在用户面前的界面将如下图所示:


    QQ-简书-1

    如果用户输入账户和密码并同意授权,将向客户端返回授权码,如:

    https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
    
    • code- 服务器在查询字符串中返回授权码
    • state- 服务器返回客户端传递的相同状态值
      还是以QQ登录授权简书为例:
      QQ-简书-2
      上图中的code即授权码。
      注:虽然 QQ授权中没有使用state,但笔者认为加上还是有好处的,可有效防止用户被诈骗。
    接下来进行令牌交换

    网站拿着上一步获取的授权码向授权服务器请求令牌(token),为此构造的URL如下:

    POST https://api.authorization-server.com/token
      grant_type=authorization_code&
      code=AUTH_CODE_HERE&
      redirect_uri=REDIRECT_URI&
      client_id=CLIENT_ID&
      client_secret=CLIENT_SECRET
    
    • grant_type = authorization_code - 此流程的授权类型是授权码
    • code= AUTH_CODE_HERE - 这是您在上一步收到的代码
    • redirect_uri = REDIRECT_URI- 必须与原始链接中提供的重定向URI相同
    • client_id = CLIENT_ID -创建应用程序时收到的客户端ID
    • client_secret = CLIENT_SECRET - 由于此请求是从服务器端代码生成的,因此包含该密钥

    授权服务器会回复一个访问令牌和到期时间

    {
      "access_token":"RsT5OjbzRn430zqMLgV3Ia",
      "expires_in":3600
    }
    

    或者是否有错误

    {
      "error":"invalid_request"
    }
    

    安全性:请注意,该服务必须要求应用程序预先注册其重定向URI。

    单页应用(SPA)

    单页应用程序(或基于浏览器的应用程序)完全在浏览器中运行。由于浏览器可以使用整个源代码,因此无法保证客户端密钥(Client Secret)的机密性,因此在这种情况下不会使用客户端密钥。该流程与上述授权代码流程完全相同,但在最后一步,授权代码用于请求访问令牌,因而不使用客户端密钥。
    注意:按照之前的做法,SPA会建议使用“隐式”流程,该流程会立即返回访问令牌,并且没有令牌交换步骤。但现在业界的最佳做法已更改为建议在没有客户机密的情况下使用授权码流。

    授权

    创建一个“登录”链接,将用户发送至:

    https://authorization-server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
    
    • code - 表示期望收到授权码
    • client_id - 创建应用程序时获得的客户端ID
    • redirect_uri - 指示在授权完成后将用户重定向的目标URI
    • scope - 一个或多个范围值,指示希望访问用户帐户的哪些部分
    • state - 由应用程序生成的随机字符串,稍后您将进行验证

    用户将看到是否同意授权的界面,如上图“QQ-简书-1”所示。如果用户同意授权,授权服务器将用户重定向至redirect_uri并附上授权码。形如:

    https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
    
    • code- 服务器在查询字符串中返回授权码
    • state- 服务器返回客户端传递的相同状态值
    令牌交换
    POST https://api.authorization-server.com/token
      grant_type=authorization_code&
      code=AUTH_CODE_HERE&
      redirect_uri=REDIRECT_URI&
      client_id=CLIENT_ID
    
    • grant_type = authorization_code - 此流程的授权类型是authorization_code
    • code = AUTH_CODE_HERE - 这是在查询字符串中收到的代码
    • redirect_uri = REDIRECT_URI - 必须与原始链接中提供的重定向URI相同
    • client_id = CLIENT_ID - 您创建应用程序时收到的客户端ID

    移动应用

    与SPA应用程序一样,移动应用也无法保持客户端密钥的机密性。因此,移动应用也必须使用不需要客户端密钥的OAuth流程。

    授权

    创建一个“登录”按钮,将用户发送至手机上的本地应用程序或该服务的移动网页。在iPhone上,应用程序可以注册一个自定义的URI方案,例如“微信://”,以便只要访问具有该协议的URL就启动本地微信应用。在Android上,应用程序可以注册URL匹配模式,如果访问了匹配模式的URL,它将启动本机应用程序。

    对于使用服务的原生应用

    如果用户安装了本机微信应用程序,请将它们指向以下URL:

    weixin://authorize?response_type=code&client_id=CLIENT_ID
      &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
    
    • response_type = code - 表示服务器期望会收到授权码
    • client_id = CLIENT_ID - 创建应用程序时收到的客户端ID
    • redirect_uri = REDIRECT_URI - 表示授权完成后返回的URI,例如weixin://authorize
    • scope = email - 一个或多个范围值,表示希望访问用户帐户的哪些部分
    • state = 1234zyx - 由应用程序生成的随机字符串,稍后将进行验证

    对于支持PKCE扩展的服务器(如果您正在构建服务器,您应该支持PKCE扩展),您还将包含以下参数。首先,创建一个“代码验证器”,它是应用程序在本地存储的随机字符串。

    • code_challenge = XXXXXXX - 这是代码验证器字符串的sha256散列的base64编码版本
    • code_challenge_method = S256 - 指示用于计算质询的散列方法,在本例中为sha256。

    请注意,您的重定向URI可能看起来像weixin://authorize协议是您的应用向操作系统注册的自定义URL方案。

    使用Web浏览器

    如果该服务没有本地应用,则可以启动移动浏览器到标准Web授权URL。请注意,您不应该在自己的应用程序中使用嵌入式Web视图,因为这不会保证用户实际上是在服务的网站上输入密码,而不是在网上诱骗网站上输入密码。

    您应该启动本地移动浏览器,或者使用新的iOS“SafariViewController”在应用程序中启动嵌入式浏览器。这个API是在iOS 9中添加的,它提供了一种在应用程序内部启动浏览器的机制,它们都显示了地址栏,因此用户可以确认它们在正确的网站上,并且还与真实的Safari浏览器共享cookie。它也阻止应用程序检查和修改浏览器的内容,因此可以认为是安全的。

    https://weixin.com/dialog/oauth?response_type=code&client_id=CLIENT_ID
      &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
    

    同样,如果服务支持PKCE,那么这些参数应该包含在上面。

    • response_type = code - 表示您的服务器期望收到授权码
    • client_id = CLIENT_ID - 创建应用程序时收到的客户端ID
    • redirect_uri = REDIRECT_URI - 表示授权完成后将用户重定向的URI,例如weixin://authorize
    • scope = email - 一个或多个范围值,指示您希望访问用户帐户的哪些部分
    • state = 1234zyx - 由应用程序生成的随机字符串,稍后将进行验证

    用户将看到授权提示。

    令牌交换

    用户看到授权提示并点击“批准”后,用户将通过类似URL的URL重定向到您的应用程序

    weixin://authorize?code=AUTHORIZATION_CODE&state=1234zyx
    

    您的移动应用应首先验证该状态是否与初始请求中使用的状态相对应,然后可以交换访问令牌的授权代码。

    令牌交换看起来与交换Web服务器应用程序案例中的代码相同,只是未发送密钥。如果服务器支持PKCE,那么您将需要包含一个附加参数,如下:

    POST https://api.authorization-server.com/token
      grant_type=authorization_code&
      code=AUTH_CODE_HERE&
      redirect_uri=REDIRECT_URI&
      client_id=CLIENT_ID&
      code_verifier=VERIFIER_STRING
    
    • grant_type = authorization_code - 此流程的授权类型是authorization_code
    • code = AUTH_CODE_HERE - 这是您在查询字符串中收到的代码
    • redirect_uri = REDIRECT_URI - 必须与原始链接中提供的重定向URI相同
    • client_id = CLIENT_ID - 您在第一次创建应用程序时收到的客户端ID
    • code_verifier = VERIFIER_STRING - 您之前用来创建code_challenge的明文字符串
      授权服务器将验证此请求并返回访问令牌。

    如果服务器支持PKCE,则授权服务器将识别出该代码是通过代码质询生成的,并且会散列提供的明文并确认散列版本与初始授权请求中发送的散列字符串相对应。这确保了对不支持密钥的客户端使用授权码流的安全性。

    其他授权类型

    密码

    OAuth 2还提供了一个“密码”授权类型,可用于直接交换访问令牌的用户名和密码。由于这显然要求应用程序收集用户的密码,因此它只能由服务本身创建的应用程序使用。例如,本机Twitter应用可以使用此授权类型登录移动或桌面应用。

    要使用密码授权类型,只需执行如下所示的POST请求:

    POST https://api.authorization-server.com/token
      grant_type=password&
      username=USERNAME&
      password=PASSWORD&
      client_id=CLIENT_ID
    
    • grant_type = password - 此流程的授权类型是密码
    • username = USERNAME - 应用程序收集的用户用户名
    • password=PASSWORD - 由应用程序收集的用户密码
    • client_id = CLIENT_ID - 创建应用程序时收到的客户端ID
      服务器以与其他授权类型相同的格式返回访问令牌。

    请注意,客户端密钥不包含在这里,因为大多数用于密码授权的场景都是移动或桌面应用程序,这种场景下无法保护客户端密钥。

    应用程序访问

    在某些情况下,应用程序可能需要访问令牌来代表自己而不是用户。例如,该服务可以为应用程序提供更新自己的信息的方式,例如他们的网站URL或图标,或者他们可能希望获得关于应用程序用户的统计信息。在这种情况下,应用程序需要一种方法,在任何特定用户的上下文之外为自己的帐户获取访问令牌。OAuth client_credentials为此提供授权类型。

    要使用客户端凭据授权类型,请进行POST请求,如下所示:

        grant_type=client_credentials&
        client_id=CLIENT_ID&
        client_secret=CLIENT_SECRET
    

    响应将包含与其他授权类型相同格式的访问令牌。

    进行身份验证的请求

    所有授权类型的最终结果是获取访问令牌。

    现在您拥有访问令牌,您可以向API发送请求。您可以使用cURL快速创建API请求,如下所示:

    curl -H "Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia" \
    https://api.authorization-server.com/1/me
    

    确保始终通过HTTPS发送请求,并且永远不要忽视无效证书。HTTPS是保护请求被拦截或修改的唯一东西。

    相关文章

      网友评论

        本文标题:ID03_OAuth2.0平铺直叙

        本文链接:https://www.haomeiwen.com/subject/xbdrrftx.html