Skip to content

OAuth2

1、OAuth

开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。 OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

目前使用最广泛的是 OAuth 2.0OAuth 1.0 已经被废弃了。本文中的 OAuth 都是指 OAuth 2.0

1.1、授权流程中的角色

  • 资源拥有者(resource owner):能授权访问受保护资源的一个实体,可以是一个人,那我们称之为最终用户;
  • 资源服务器(resource server):存储受保护资源,客户端通过access token请求资源,资源服务器响应受保护资源给客户端;
  • 授权服务器(authorization server):成功验证资源拥有者并获取授权之后,授权服务器颁发授权令牌(Access Token)给客户端。
  • 客户端(client):第三方应用,也可以是它自己的官方应用;其本身不存储资源,而是资源拥有者授权通过后,使用它的授权(授权令牌)访问受保护资源,然后客户端把相应的数据展示出来/提交到服务器。

1.2、令牌与密码

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

  • 令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
  • 令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
  • 令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

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

2、授权模式

OAuth 的核心就是向第三方应用颁发令牌。OAuth 2.0 协议根据使用不同的适用场景,规定了四种获得令牌的流程。你可以选择最适合自己的那一种,向第三方应用颁发令牌。

  • 授权码模式(Authorization Code Grant
  • 隐式模式(Implicit Grant
  • 密码模式式(Resource Owner Password Credentials Grant):
  • 客户端凭证模式(Client Credentials Grant

2.1、授权码模式

image-20231016104011207

第一步:用户访问页面或者出发认证地址

第二步:访问的页面将请求重定向到认证服务器

第三步:用户登录成功只有,认证服务器向用户展示授权页面,等待用户授权

第四步:用户授权,认证服务器生成一个 code 和带上 client_id 发送给应用服务器。然后,应用服务器拿到 code,并用 client_id 去后台查询对应的client_secret

第五步:将 code,client_id,client_secret传给认证服务器换取 access_tokenrefresh_token

第六步:将 access_tokenrefresh_token 传给应用服务器

第七步:验证 token,访问真正的资源页面

image-20231016104033951

2.2、隐式模式

隐式模式主要流程如下图所示:

image-20231016104052956

  • 第一步:用户访问页面时,重定向到认证服务器。
  • 第二步:认证服务器给用户一个认证页面,等待用户授权。
  • 第三步:用户授权,认证服务器想应用页面返回 Token
  • 第四步:验证 Token,访问真正的资源页面

image-20231016104108019

2.3、密码模式

密码模式主要流程如下图所示:

image-20231016104135177

  • 第一步:用户访问用页面时,输入第三方认证所需要的信息(QQ/微信账号密码)
  • 第二步:应用页面那种这个信息去认证服务器授权
  • 第三步:认证服务器授权通过,拿到 token,访问真正的资源页面

优点:不需要多次请求转发,额外开销,同时可以获取更多的用户信息。(都拿到账号密码了)

缺点:局限性,认证服务器和应用方必须有超高的信赖。(比如亲兄弟?)

应用场景:自家公司搭建的认证服务器

2.4、客户端凭证模式

客户端凭证模式主要流程如下图所示:

image-20231016104222594

  • 第一步:用户访问应用客户端
  • 第二步:通过客户端定义的验证方法,拿到 token,无需授权
  • 第三步:访问资源服务器 A
  • 第四步:拿到一次 token 就可以畅通无阻的访问其他的资源页面。

这是一种最简单的模式,只要 client 请求,我们就将 AccessToken 发送给它。这种模式是最方便但最不安全的模式。因此这就要求我们对 client 完全的信任,而 client 本身也是安全的。

因此这种模式一般用来提供给我们完全信任的服务器端服务。在这个过程中不需要用户的参与。

3、授权验证

说明

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

即:要在系统中为该客户端分配 ID(client ID)和客户端密钥(client secret

换句话说 oauth-client-details 表中要有该客户端对应的数据

3.1、授权码模式验证

指的是第三方应用先申请一个授权码,然后再用该码获取令牌。这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

3.1.1、客户端向资源端申请 code

这里所说的客户端,是指任意想要使用 平台 的应用系统。

主要逻辑:假设,某网站或者系统(客户端),我们称之为 A 。Dante Cloud 向 A 网站提供一个认证链接,A 网站会把这个连接做成一个按钮,用户点击后就会根据这个连接跳转到 Dante Cloud,在 Dante Cloud 认证通过后授权用户数据给 A 网站使用。

上面所说的连接,就是如下的认证连接:

http://192.168.101.10:8847/scaffold/oauth/authorize?response_type=code&client_id=010e659a-4005-4610-98f6-00b822f4758e&redirect_uri=http://localhost:9999/passport/login&scope=all

参数

  • response_type:必选。值固定为“code”。
  • client_id:必选参数(第三方应用的标识 ID,告诉服务器谁需要得到授权)
  • state:Client 提供的一个字符串,服务器会原样返回给 Client。这个要自己实现,用于防止恶意攻击。
  • redirect_uri:必选参数(授权成功后的重定向地址)
  • scope:可选参数(表示授权范围)

3.1.2、资源端返回 code 给客户端

可以采用以下两种方式进行验证:

  • 第一种:如果有想要接入的系统,那么就在这个系统中做一个图标按钮,点击后跳转到上面的地址。(比如:很多系统都支持微信登录,那么在页面上就会有一个微信的图标按钮,点击后跳转到一个地址)
  • 第二种:如果没有想接入的系统,可以将上面的地址输入到浏览器,获取信息后配合 Postman 等工具进行验证。

下面采用第二种浏览器的方式

在浏览器中输入上面的地址,会跳出如下登录界面。

image-20231016105220007

输入用户名,密码和验证码进行用户验证。

可以使用系统默认用户:system 密码:123456

登录成功后,会跳转到授权页面进行授权,如下图所示:

image-20231016105255216

授权成功后,就会跳转到一个新的地址,同时在地址的后面会跟随生成的 code, 如下所示。这个地址就是系统中设置的 redirect_uri

url
http://localhost:9999/passport/login?code=P6dxH5
http://localhost:9999/passport/login?code=P6dxH5

3.1.3、客户端根据 code 向资源端请求令牌

在你习惯的工具中,输入下面的地址:

http://192.168.101.10:8847/scaffold/oauth/token?client_id=010e659a-4005-4610-98f6-00b822f4758e&client_secret=04165a07-cffd-45cf-a20a-1c2a69f65fb1&grant_type=authorization_code&code=P6dxH5&scope=all&redirect_uri=http://localhost:9999/passport/login

参数

  • grant_type:必选参数(固定值“authorization_code”)
  • code : 必选参数(上一步 Response 中响应的 code,避免在请求的过程中被篡改)
  • redirect_uri:必选参数(必须和 Request 中提供的 redirect_uri 相同)
  • client_id:必选参数(必须和上一步 Request 中提供的 client_id 相同,不能 token 和授权码请求的第三方应用不一致)

下图以 Postman 为例:

image-20231016105459055

3.1.4、资源端向客户端返回令牌及明细

返回如下所示 Token 则表示验证成功

json
{
  "access_token": "***",
  "token_type": "bearer",
  "refresh_token": "***",
  "expires_in": 43199,
  "scope": "all",
  "license": "scsffold",
  "openid": "1",
  "jti": "4bb54345-dd24-4347-8d26-655e5d4085f7"
}
{
  "access_token": "***",
  "token_type": "bearer",
  "refresh_token": "***",
  "expires_in": 43199,
  "scope": "all",
  "license": "scsffold",
  "openid": "1",
  "jti": "4bb54345-dd24-4347-8d26-655e5d4085f7"
}

参数

  • access_token:访问令牌(服务器端加密过的字符串)。
  • refresh_token:刷新令牌(刷新令牌的字符串)
  • expires_in:过期时间(令牌的过期时间)

3.2、隐式模式验证

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

3.2.1、客户端向资源端申请 Token

在浏览器中输入如下的认证连接:

http://192.168.101.10:8847/scaffold/oauth/authorize?response_type=token&client_id=010e659a-4005-4610-98f6-00b822f4758e&redirect_uri=http://www.baidu.com&scope=all

参数

  • response_type:必选。值固定为“token”。
  • client_id:必选参数(第三方应用的标识 ID,告诉服务器谁需要得到授权)
  • state:Client 提供的一个字符串,服务器会原样返回给 Client。这个要自己实现,用于防止恶意攻击。
  • redirect_uri:必选参数(授权成功后的重定向地址)
  • scope:可选参数(表示授权范围)

3.2.2、到认证服务器认证

在浏览器中输入上面的地址,会跳出登录界面。

输入用户名,密码和验证码进行用户验证。

可以使用系统默认用户:system 密码:123456

登录成功后,会跳转到授权页面进行授权,如下图所示:

image-20231016105255216

3.2.3、资源端转向客户端并传递 token

授权成功后,就会跳转到一个新的地址,同时在地址的后面会跟随生成的 token, 如下所示。这个地址就是系统中设置的 redirect_uri

https://www.baidu.com/#access_token=eee&token_type=bearer&expires_in=42511&license=scaffold&openid=1&jti=4bb54345-dd24-4347-8d26-655e5d4085f7

上面 URL 中,access_token 参数就是令牌

3.3、密码模式验证

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

直接使用如下地址获取 Token 即可

http://192.168.101.10:8847/scaffold/oauth/token

参数

  • username:必选参数(登录用户名)
  • password:必选参数(登录用户密码)
  • grant_type:必选参数(固定值“password”)
  • client_id:必选参数
  • client_secret:必选参数
  • scope:可选参数(表示授权范围)

操作如下图所示:

image-20231016110139561

3.4、客户端凭证模式验证

这种模式直接根据 client 的 id 和密钥即可获取 token,无需用户参与 这种模式比较合适消费 api 的后端服务,比如拉取一组用户信息等

直接使用如下地址获取 Token 即可

http://192.168.101.10:8847/scaffold/oauth/token

参数

  • grant_type:必选参数(固定值“client_credentials”)
  • client_id:必选参数
  • client_secret:必选参数
  • scope:可选参数(表示授权范围)

image-20231016110246733