OAuth 2.0 的一个简单解释

2020-06-29

Ann Ann

一、快递员问题

我住在一个大型的居民小区。

小区有门禁系统。

进入的时候需要输入密码。

我经常网购和外卖,每天都有快递员来送货。我必须找到一个办法,让快递员通过门禁系统,进入小区。

如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。

有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?

二、授权机制的设计

于是,我设计了一套授权机制。

第一步,门禁系统的密码输入器下面,增加一个按钮,叫做“获取授权”。快递员需要首先按这个按钮,去申请授权。

第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。

我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。

第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。

第四步,快递员向门禁系统输入令牌,进入小区。

有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。

三、互联网场景

我们把上面的例子搬到互联网,就是 OAuth 的设计了。

首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的“门禁系统”。

其次,快递员(或者说快递公司)就是第三方应用,想要穿过门禁系统,进入小区。

最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。

简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

四、令牌与密码

令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。

(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

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

OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的方式,适用于不同的互联网场景。下一篇文章,我就来介绍这四种类型,并给出代码实例。

(完)

Oauth 2.0 的四种方式

上一篇文章介绍了 OAuth 2.0 是一种授权机制,主要用来颁发令牌(token)。本文接着介绍颁发令牌的实务操作。

下面我假定,你已经理解了 OAuth 2.0 的含义和设计思想,否则请先阅读这个系列的上一篇文章。

RFC 6749

OAuth 2.0 的标准是 RFC 6749 文件。该文件先解释了 OAuth 是什么。

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

这段话的意思就是,OAuth 的核心就是令牌,然后,它接着写道:

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

也就是说,OAuth 2.0 规定了四种获得令牌的流程。你可以选择最适合自己的哪一种,向第三方应用颁发令牌。下面就是这四种授权方式。

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

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

第一种授权方式:授权码

授权码(authorization code)是最常用流程,因为适用于那些有服务端的 Web 应用。它的安全性最高,因为授权令牌储存在服务端,不会暴露,而且所有与资源服务器的通信都在服务端完成。

基本流程是这样:用户访问 A 网站,授权它获取自己在 B 网站的资料。A 网站会提供一个链接,让用户点击跳转到 B 网站。

这个链接需要提供一些参数(不同服务的要求可能不一样)。

https://b.com/oauth/authorize?
  response_type=code&
  client_id=CLIENT_ID&
  redirect_uri=CALLBACK_URL&
  scope=read
  • response_type:表示返回类型,这里为code,即授权码。
  • client_id:第三方应用的备案 ID。
  • redirect_uri:授权被接受或拒绝后的跳转网址
  • scope:权限范围

用户跳转到 B 网站以后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳转到redirect_uri指定的跳转网址。跳转时,会传回一个授权码,就像下面这样。

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

A 网站拿到授权码以后,会在服务端向 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

上面 URL 的参数含义如下。

  • client_id:第三方应用的备案 ID
  • client_secret:第三方应用的密钥
  • grant_type:授权类型,AUTHORIZATION_CODE表示授权码
  • code:上一步拿到的授权码
  • redirect_uri:令牌颁发后的回调网址

B 网站收到请求以后,就会向 A 网站颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据。

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

A 网站收到 JSON 数据以后,从里面取出了令牌(access_token字段)。有了令牌以后,就可以向 B 网站的 API 请求数据了。这时,每个发到 API 的请求,都必须带有令牌,做法是写在头信息Authorization字段里面,就像下面这样。

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

第二种方式:隐藏式

上面的授权码方式,需要把令牌储存在服务端。但是,有些应用没有服务端,是纯粹的前端应用(即 SPA)。这时,标准允许第二种方式,跳过授权码,直接向前端应用传送令牌。这种方式下,授权码是没有的,所以称为“隐藏式”。

首先,用户访问 A 网站,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,要求直接返回令牌。

用户点击以后,到了 B 网站,点击按钮,同意给予 A 网站授权。这时,B 网站就会跳转到redirect_uri参数指定的网址,并且把令牌作为 URL 参数,传给 A 网站。

https://dropletbook.com/callback?token=ACCESS_TOKEN

A 网站拿到令牌以后,就可以在前端脚本使用令牌,获取 B 网站的数据。

这种方式把令牌直接传给前端,所以是很不安全的。因此,只能用于一些对于安全要求不高的场合,并且令牌的有效期必须非常短,通常就是会话期间有效,会话结束,令牌就会失效。

第三种方式:密码式

如果你高度信任某个应用,标准也允许用户把用户名和密码,直接告诉该应用。该应用使用你的密码,直接获取数据,这种方式称为“密码式”。

首先,用户访问 A 网站,该网站想要用户储存在 B 网站的数据,于是要求用户给出 B 网站的用户名和密码。拿到以后,就直接向 B 网站请求令牌。

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

上面的 URL 里面,grant_type是授权类型,password表示采用“密码式”,usernamepassword是用户名和密码。

然后,B 网站验证用户和密码,通过以后,不需要跳转,而是作为对于 A 网站请求的 HTTP 回应,直接给出令牌。

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

第四种方式:凭证式

凭证式适用于没有 Web 前端的后端应用,必须在命令行下请求 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是客户端的 ID 和密钥。

B 网站验证客户端以后,直接返回令牌。这种方式不要求用户信息,因此给出的令牌,是针对第三方应用的,而不是针对用户的。

令牌的使用

curl -X POST -H "Authorization: Bearer ACCESS_TOKEN""https://api.digitalocean.com/v2/$OBJECT"

更新令牌

令牌到期以后,如果让用户重新申请,很可能体验不好,而且可能也没有必要。

因此,OAuth 2.0 允许用户在获得令牌以后,在到期前,自动更新令牌。

具体方法是,B 网站返回令牌的时候,一次性返回两个。一个用于获取数据,另一个用于更新。

然后,用户使用 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表示这个操作是更新令牌,client_idclient_secret表示客户端身份,refresh_token参数就是用于更新的令牌。

B 网站验证请求以后,就会返回新的令牌。

令牌和密码

令牌和密码都能用来认证身份,都能获取数据。

它们的区别?令牌是针对 session 的,对话期间有效,而且是有授权范围的。password 是长期有效的。

OAuth:颁发 access token 代替 password

Authorization Grant authorization code

角色

  • resource owner 用户本身
  • HTTP service 服务商,拥有资源的服务器
  • Third-party application(client)

resource server 资源服务器

允许其他人拿到你的私人数据。

身份提供商使用

近期开课hot

Python零基础入门

start2025/02/12 03:14 (Sydney)

Web全栈班24期 NodeJS方向

start2024/12/08 11:30 (Sydney)

logo

Follow Us

linkedinfacebooktwitterinstagramweiboyoutubebilibilitiktokxigua

We Accept

/image/layout/pay-paypal.png/image/layout/pay-visa.png/image/layout/pay-master-card.png/image/layout/pay-stripe.png/image/layout/pay-alipay.png

地址

Level 10b, 144 Edward Street, Brisbane CBD(Headquarter)
Level 8, 11 York st, Wynyard, Sydney CBD
Business Hub, 155 Waymouth St, Adelaide SA 5000

Disclaimer

footer-disclaimerfooter-disclaimer

JR Academy acknowledges Traditional Owners of Country throughout Australia and recognises the continuing connection to lands, waters and communities. We pay our respect to Aboriginal and Torres Strait Islander cultures; and to Elders past and present. Aboriginal and Torres Strait Islander peoples should be aware that this website may contain images or names of people who have since passed away.

匠人学院网站上的所有内容,包括课程材料、徽标和匠人学院网站上提供的信息,均受澳大利亚政府知识产权法的保护。严禁未经授权使用、销售、分发、复制或修改。违规行为可能会导致法律诉讼。通过访问我们的网站,您同意尊重我们的知识产权。 JR Academy Pty Ltd 保留所有权利,包括专利、商标和版权。任何侵权行为都将受到法律追究。查看用户协议

© 2017-2024 JR Academy Pty Ltd. All rights reserved.

ABN 26621887572