主流的认证授权技术

核心概念

  • 认证(authentication)

  • 授权(authorization)

  • 凭证(credential)

认证(authentication)

认证的目的是确定进入系统的用户的身份。

常见认证方式有Basic(账号密码)、Bearer(OAuth2),Http协议本身也支持用户的认证。

Http 认证框架

http是无状态协议,在访问资源时,为了保护资源的安全,需要对用户身份进行认证。rfc7235规范定义了如何通过http协议认证用户身份。主要涉及4个Hearder和2个Http Status code。

// Header
+---------------------+----------+----------+-------------+
| Header Field Name   | Protocol | Status   | Reference   |
+---------------------+----------+----------+-------------+
| Authorization       | http     | standard | Section 4.2 |
| Proxy-Authenticate  | http     | standard | Section 4.3 |
| Proxy-Authorization | http     | standard | Section 4.4 |
| WWW-Authenticate    | http     | standard | Section 4.1 |
+---------------------+----------+----------+-------------+

// Status Code
+-------+-------------------------------+-------------+
| Value | Description                   | Reference   |
+-------+-------------------------------+-------------+
| 401   | Unauthorized                  | Section 3.1 |
| 407   | Proxy Authentication Required | Section 3.2 |
+-------+-------------------------------+-------------+

当我们要访问某个资源时,资源服务器会在响应状态码401或407,请求头里返回支持的认证方式,格式如下:

401 Unauthorized
WWW-Authenticate: <认证方案> realm=<保护区域的描述信息>, <认证方案> realm=<保护区域的描述信息>

407 Proxy Authentication Required
Proxy-Authenticate: <认证方案> realm=<保护区域的描述信息>, <认证方案> realm=<保护区域的描述信息>

WWW是对资源服务器的认证,Proxy是对代理服务器的认证。

收到响应后,客户端讲要在输入身份凭证内容。然后在请求投带上凭证,再次请求资源服务器。

Authorization: <认证方案> <凭证内容>
Proxy-Authorization: <认证方案> <凭证内容>

认证方案

认证方案就是身份认证方式,可以是账号密码、授权码、生物指纹等。已注册的方式在这能看到:Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry。常见的有Basic、Bearer等。

Basic 基本认证 [rfc7617]

触发Basic认证

WWW-Authenticate: Basic realm="foo", charset="UTF-8"

携带认证信息:传入base64(账号:密码)的结果

Authorization: Basic dGVzdDoxMjPCow==

列子里dGVzdDoxMjPCow==是base64(test:123£)的结果

Bearer 认证 [rfc6750]

Bearer是一种基于OAuth2.0认证框架([rfc6749])来证明身份的方式。认证过程如下:

+--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+
  1. 触发Bearer认证

WWW-Authenticate: Bearer realm="example"
  1. 客户端请求资源服务器拿到授权code后,通过授权code请求认证服务器拿到acess_token

{
   "access_token":"mF_9.B5f-4.1JqM",
   "token_type":"Bearer",
   "expires_in":3600,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
  1. 携带认证信息:

Authorization: Bearer mF_9.B5f-4.1JqM

生物认证 [webauthn]

WebAuthn全称是Web Authentication API,是由W3C和FIDO在 Google、Mozilla、Microsoft、Yubico 等公司的参与下编写的规范。WebAuthn是一组在服务器、浏览器和身份验证器之间启用无密码身份验证的技术。一般使用非对称加密技术,用公私钥代替传统的密码,来注册和验证用户。主流浏览器Chrome、Firefox、Edge 和 Safari 都支持 WebAuthn。

注册流程

  1. 用户进入注册页面,填写个人资料后,提交给服务端。

  2. 服务端收到个人资料后,生成Challenge(随机字符串)和凭证 ID(用户的userID)返回给客户端。

  3. 客户端将Challenge 、凭证 ID和服务器域名信息发送给验证器(Authenticator),验证器可以是FaceID、实体秘钥等认证设备,存在多个设备可以让用户选择一种验证器。

  4. 验证器判断域名+凭证 ID是否存在密钥对,不存在就创建新的密钥对(公私钥)。验证器将私钥存在域名+凭证 ID下,再用私钥签名Challenge,将签名结果、凭证 ID和公钥返回给客户端(浏览器)。

  5. 浏览器把结果传给服务器

  6. 服务器取出凭证 ID对应的Challenge,用公钥签名Challenge,将结果和浏览器返回的签名比较,一致表明验证通过。然后,把凭证 ID对应的公钥存下来。

认证流程

  1. 用户进入登录页面,输入用户名后点击登录

  2. 服务器返回凭证 ID(userID)和Challenge

  3. 浏览器将域名、凭证 ID(userID)和Challenge发给验证器

  4. 验证器通过域名、凭证 ID找到私钥,通过私钥签名Challenge,将签名结果返回给浏览器。

  5. 浏览器将将签名结果、凭证 ID发给服务器

  6. 服务器取出凭证 ID对应的Challenge,用公钥签名Challenge,将结果和浏览器返回的签名比较,一致表面验证通过,允许用户登录。

授权(authorization)

授权是系统对用户赋予权限的过程,对于平台系统会注重权限隔离,包括功能权限、数据权限等。C端应用一般只访问自身数据,所以权限比较简单,认证通过就能获得账号的全部处理权限。

有一点和平台系统类似,当应用需要和三方进行交互时,尤其需要从三方系统获取用户操作权限。

例如,通过三方注册登录的场景,用户要让应用知道他在三方系统上有账号和个人信息。最简单的做法是把账号密码提供给应用,让应用直接登录三方账号得到用户信息。很明细这种方案是不安全的,而OAuth2.0认证授权协议就是解决这个问题的最佳实践。

OAuth 2.0 认证授权

OAuth使用token来代替密码,避免密码的泄露。认证过程如下:

+--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+

角色

  • 第三方应用:需要获取资源的应用

  • 资源拥有者(resource owner):能够授予对受保护资源的访问权限的实体。当资源所有者是个人时,它称为最终用户。

  • 授权服务器(authorization server):在成功对资源所有者进行身份验证并获得授权后,向客户端颁发访问令牌的服务器。

  • 资源服务器(resource server):托管受保护资源的服务器,能够使用访问令牌接受和响应受保护的资源请求。

  • 操作代理(client):代表资源所有者并授权发出受保护资源请求的应用程序。

以app通过Google授权登录为例,这里的角色分别是:

  • 第三方应用:app

  • 资源拥有者:当前用户

  • 授权服务器:Google授权服务

  • 资源服务器:Google账号服务

  • 操作代理:浏览器

概念

  • 授权码

  • 访问令牌(Access Token)

  • 刷新令牌(Refresh Token)

授权码

授权码是用户授权成功之后,授权服务器颁发给客户端的凭证。它无法直接从资源服务器获得到资源,得通过授权服务器获取访问令牌。在获取访问令牌时,需要验证三方应用的身份。所以,直接截获授权码并没有用

访问令牌

访问令牌是用于访问受保护资源的凭证。表示访问特定资源的权限范围和有效时间。它可以是一个简单的id标识,也可以自包含授权信息(JWT)。

刷新令牌

刷新令牌是用于获取新的访问令牌的凭证。它是可选项,如果颁发了,就能通过刷新令牌向授权服务器获得新的访问令牌。

  +--------+                                           +---------------+
  |        |--(A)------- Authorization Grant --------->|               |
  |        |                                           |               |
  |        |<-(B)----------- Access Token -------------|               |
  |        |               & Refresh Token             |               |
  |        |                                           |               |
  |        |                            +----------+   |               |
  |        |--(C)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(D)- Protected Resource --| Resource |   | Authorization |
  | Client |                            |  Server  |   |     Server    |
  |        |--(E)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(F)- Invalid Token Error -|          |   |               |
  |        |                            +----------+   |               |
  |        |                                           |               |
  |        |--(G)----------- Refresh Token ----------->|               |
  |        |                                           |               |
  |        |<-(H)----------- Access Token -------------|               |
  +--------+           & Optional Refresh Token        +---------------+

认证方式

OAuth2.0为支持不同应用场景,安全力度从高到低分为4种模式:

  • 授权码模式

  • 隐式授权模式

  • 密码模式

  • 客户的模式

授权码模式

这个OAuth2.0的完全体,也是最严谨的模式。

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)

在发起授权前,第三方应用要在授权服务器上注册自己,得到一个secretKey,表示应用的身份。

在客户端拿到授权码后,第三方应用拿着授权码+secretKey向授权服务获取访问令牌。

授权成功后,第三方应用会获得access_token(有效期短)和refresh_token(有效期长)。

为什么会有两种令牌?

这里涉及token的安全。不记名token在颁发后,在过期前都是有效。这就导致token泄露后,账号存在很大风险。一种做法是控制token的有效期较短(access_token),又要避免频繁让用户授权。折中方案就是定时去刷新access_token,在刷新的时候判断用户授权是否有效。

隐式授权模式

这种模式是为了应对没有第三方应用的情况,对于前端应用,直接从授权服务器得到access_token,省略了授权码的环节。

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+

为了尽量保证access_token的安全,access_token放在回调URL里的Fragment,避免后续客户的发起请求,把access_token给带出去了。

密码模式

这种模式是用户直接提供密码给第三方应用,应用通过密码向授权服务器获取access_token。这是高度信任客户端的情况下才会使用。

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

密码模式需要用户提供密码,在C端应用基本不可行。

客户的模式

这种模式是客户的直接向授权服务器申请访问access_token,不需要任何授权。

     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

这种模式下,授权过程要保持完全可信。只有在后的微服务的架构下,服务间调用需要access_token,服务在调用之前会从授权服务器获取access_token。

凭证(credential)

凭证是系统和用户之间的承诺,授权过程就是为了获取这份承诺。

凭证的形式

  • Cookie-Session

  • JWT

Cookie-Session [rfc6265]

在web领域,这是主流的存储用户状态(身份)的方案。用户信息存在服务端的session里,把sessionId通过cookie传给客户端。每次Http请求都会带上cookie信息,服务端讲能知道请求来自哪个用户。

所以,对于授权通过的用户,在session加上授权信息。只要cookie不泄露,用户信息就是安全的。

存在的问题:

  1. 对于移动端,用户操作的不再是浏览器,所以像cookie的维护、同源策略的安全保护都需要重新实现。

  2. 分布式场景下,session面临分布式存储和校验问题。

JWT [rfc7519]

JWT是JSON web token大家简写,它是主流的token格式,也是移动端使用最广泛的用户身份存储方案。

常用的做法是用jwt做认证:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJpY3lmZW5peCIsInNjb3BlIjpbIkFMTCJdLCJleHAiOjE1ODQ5NDg5NDcsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfQURNSU4iXSwianRpIjoiOWQ3NzU4NmEtM2Y0Zi00Y2JiLTk5MjQtZmUyZjc3ZGZhMzNkIiwiY2xpZW50X2lkIjoiYm9va3N0b3JlX2Zyb250ZW5kIiwidXNlcm5hbWUiOiJpY3lmZW5peCJ9.539WMzbjv63wBtx4ytYYw_Fo1ECG_9vsgAn8bheflL8
协议格式
  • Header

  • Payload

  • Signature

Header

Header包含type和alg两个字段。type是固定的“JWT”,alg是签名的算法,除了HS256还支持RS256、ES256等,在JSON Web Tokens - jwt.io上能看到完整的列表。

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

payload的JWT具体的内容,内容是可以自定义的,RFC 7519推荐了7种类型:

  • iss(Issuer):签发人。

  • exp(Expiration Time):令牌过期时间。

  • sub(Subject):主题。

  • aud (Audience):令牌受众。

  • nbf (Not Before):令牌生效时间。

  • iat (Issued At):令牌签发时间。

  • jti (JWT ID):令牌编号。

Signature

使用密钥,通过指定的算法对header和payload进行签名。确保JWT里的内容没有被其他人篡改。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload) , secret)
编解码示例

字段

原始

base64url编码

header

{"alg":"HS256","typ":"JWT"}

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

payload

{"sub":"1234567890","name":"John Doe","iat":1516239022}

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ==

secret

abcd

JWT结果:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cq-uoLxOu3V4RjxnbUAFZ36aSZ24BXiAH8RFDYVA6XU

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李昂的数字之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值