OAuth 2.0 认证

简单介绍

Oauth 2.0是目前最流行的授权机制,用来授权第三方应用,获取用户的数据。

OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不 需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。OAuth2.0是OAuth协议的延续版本,但不向 后兼容OAuth 1.0即完全废止了OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服 务。

授权机制设计

OAuth 2.0是一种授权机制,主要用来颁发令牌(token),该机制允许用户将存储在一个服务提供商中的信息给另一个服务提供商使用。

在这里我们用微信登录QQ音乐为例(微信授权登录是通过OAuth2.0机制实现的)。当我们在登录QQ音乐界面选择微信登录时,QQ音乐会发起一个微信授权登录请求,这时候我们的微信就会跳出是否允许登陆的界面,当我们点击允许后,微信就会返回一个access_token,然后QQ音乐使用access_token获取我们的微信名、微信头像、微信好友列表等信息,当然这只是个大概的步骤,但可以 帮我们更快的理解OAuth机制。

令牌与密码

令牌(Token)和密码(password)作用都是相同的,都可以进行登录,但是还是有所区别:

  1. 令牌是短期的,一旦超过有效期就会失效。密码则是长期有效,如果用户不对密码进行修改,就不会发生改变。
  2. 令牌可以被数据所有者撤销,撤销即时效,比如说,当我们在微信上,选择登出QQ音乐,QQ音乐上的access_token就会失效,具体表现就是QQ音乐会让我们重新登录。而密码用户不更改就不会被撤销。
  3. 令牌有权限范围(scope),比如说QQ音乐(第三方服务)使用access_token可以获取我们微信上的用户名,微信头像,好友等信息,但是无法获取微信的密码等私密信息,也无法更改我们的微信名,密码等信息。而密码具有完整的权限,可以获取该密码对应用户所有信息并更改。所以在网络服务中,只读属性的令牌比读写属性的令牌更加安全。

以上令牌具有的属性,保证了令牌既可以让第三方应用获取权限,同时又随时可控,不会危机授权方系统的安全。这些就是OAuth 2.0的优点。

注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。

OAuth 2.0四种授权方式

OAuth2.0的标准是RFC 6749文件,该文件对OAuth 2.0进行了解释,并规定了四种授权方式。

RFC 6749对OAuth解释为:

OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者。......资源所有者同意以后,资源服务器可以向客户端颁发令牌。客户端通过令牌,去请求数据。

这段话的意思就是,OAuth 的核心就是向第三方应用颁发令牌。然后,RFC 6749 接着写道:

(由于互联网有多种场景,)本标准定义了获得令牌的四种授权方式(authorization grant )。

OAuth 2.0规定了四种获取令牌的流程,从而向第三方应用颁发令牌。这四种方式分别是:

  1. 授权码(authorization-code)
  2. 隐藏式(implicit)
  3. 密码式(password)
  4. 客户端凭证(client credentials)

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

授权码

授权码(authorization-code)方式,是指第三方应用先申请一个授权码,然后再使用改码去获取令牌。

这种方式是最常用的流程,安全性也最高,微信得授权就是使用的该方式。它适用于有后端的Web应用。授权码通过前端传送,令牌则是存储到后端,而且所有的资源获取都是后端携带令牌进行获取,避免了令牌的泄漏。

  • Step 1: A 网站提供一个链接,用户点击后就会跳转到 B 网站,B网站授权用户数据给 A 网站使用。下面是 A 网站跳转 B 网站的一个示意链接(该请求由A网站前端发起):

    https://b.com/oauth/authorize?
      response_type=code&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    response_type参数表示要求返回授权码(code),client_id参数让 B 知道是谁在请求,redirect_uri参数是 B 接受或拒绝请求后的跳转网址,scope参数表示要求的授权范围(这里是只读)。

  • Step 2: 用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户同意后,B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码模板如下:

    https://a.com/callback?code=AUTHORIZATION_CODE
    

    code参数就是授权码。

  • Step 3: A网站拿到授权码后(第一步中的redirect_uri即为A网站后端接收授权码(code)的API接口),然后A使用code在后端,向B网站请求令牌。模板如下:

    https://b.com/oauth/token?
     client_id=CLIENT_ID&
     client_secret=CLIENT_SECRET&
     grant_type=authorization_code&
     code=AUTHORIZATION_CODE&
     redirect_uri=CALLBACK_URL
    

    client_id参数和client_secret参数用来让 B 确认 A 的身份(client_secret参数是保密的,因此只能在后端发请求),grant_type参数的值是AUTHORIZATION_CODE,表示采用的授权方式是授权码,code参数是上一步拿到的授权码,redirect_uri参数是令牌颁发后的回调网址。

  • Step 4: B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据。模板如下:

    {    
      "access_token":"ACCESS_TOKEN",
      "token_type":"bearer",
      "expires_in":2592000,
      "refresh_token":"REFRESH_TOKEN",
      "scope":"read",
      "uid":100101,
      "info":{...}
    }
    

    access_token字段就是令牌,A 网站在后端就拿到了令牌。其它字段含义为:token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

微信 OAuth2.0 授权登录目前支持 authorization_code 模式,适用于拥有 server 端的应用授权。这里我们以微信为例,整体流程为:

1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;

2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;

3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

获取 access_token 时序图:

隐藏式

该模式在其它博客中,有的称作"简化模式",不过在RFC 6749的1.3.2章节,它的名字是implicit

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。

  • Step 1: A 网站提供一个链接,用户点击后就会跳转到 B 网站,B网站授权用户数据给 A 网站使用。

    https://b.com/oauth/authorize?
      response_type=token&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    response_type参数为token,表示要求直接返回令牌。

  • Step 2: 用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri参数指定的跳转网址,并且把令牌以Hash的形式存放在重定向URL的fargment中传给 A 网站。

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

    token参数就是令牌,A 网站直接在前端拿到令牌。

fragment 主要是用来标识 URI 所标识资源里的某个资源,在 URI 的末尾通过 (#)作为 fragment 的开头, 其中 # 不属于 fragment 的值。如[https://domain/index#L18] 这个 URI 中 L18 就是 fragment 的值。js通过响应浏览器地址栏变化的方式能获取到fragment。

这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。

密码式

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。

第一步,A 网站要求用户提供 B 网站的用户名和密码。拿到以后,A 就直接向 B 请求令牌。

https://oauth.b.com/token?
  grant_type=password&
  username=USERNAME&
  password=PASSWORD&
  client_id=CLIENT_ID

上面 URL 中,grant_type参数是授权方式,这里的password表示"密码式",usernamepassword是 B 的用户名和密码。

第二步,B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 因此拿到令牌。

这种方式需要用户给出自己的用户名/密码,显然风险很大,因此只适用于其他授权方式都无法采用的情况,而且必须是用户高度信任的应用。

客户端凭证

最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌。

第一步,A 应用在命令行向 B 发出请求。

https://oauth.b.com/token?
  grant_type=client_credentials&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET

上面 URL 中,grant_type参数等于client_credentials表示采用凭证式,client_idclient_secret用来让 B 确认 A 的身份。

第二步,B 网站验证通过以后,直接返回令牌。

这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

令牌使用

A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。

此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。

curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"

上面命令中,ACCESS_TOKEN就是拿到的令牌。

更新令牌

令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许用户自动更新令牌。

具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

https://b.com/oauth/token?
  grant_type=refresh_token&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET&
  refresh_token=REFRESH_TOKEN

上面 URL 中,grant_type参数为refresh_token表示要求更新令牌,client_id参数和client_secret参数用于确认身份,refresh_token参数就是用于更新令牌的令牌。

B 网站验证通过以后,就会颁发新的令牌。

本文参考:

posted @   joudys  阅读(190)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示