理解基于令牌(token)的认证
基于令牌的身份验证更简单。令牌有几种实现方式,但 JSON Web
令牌 (JWT
) 是最常见的一种。基于令牌的身份验证是无状态的。这意味着服务器端不持久化任何会话,因为状态存储在客户端的令牌内。服务器的责任仅仅是使用密钥创建 JWT
并将其发送给客户端。客户端将 JWT
存储在本地存储或客户端 Cookie
中,并在每次发出请求时将其包含在标头中。然后,服务器验证 JWT
并发送响应。
但是,什么是 JWT
?它是如何工作的?让我们在下一节中找到答案。
什么是 JSON Web 令牌?
要理解 JWT
的工作原理,我们首先应该理解它是什么。简而言之,JWT
是一个哈希 JSON
对象字符串,由标头、载荷和签名组成。JWT
按照以下格式生成:
header.payload.signature
标头通常由两部分组成:类型(type
)和 算法(algorithm
)。类型是 JWT
,算法可以是 HMAC
、SHA256
或 RSA
,这是一种使用密钥对令牌进行签名的哈希算法,例如:
{
"typ": "JWT",
"alg": "HS256"
}
载荷是 JWT
中存储信息(或声明)的部分,例如:
{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd",
"name": "Jane Doe"
}
在这个例子中,我们在载荷中只包含了两个声明。你可以根据需要放入任意数量的声明。你包含的声明越多,JWT
的大小就越大,这可能会影响性能。还有其他可选的声明,例如 iss
(签发者)、sub
(主题)和 exp
(过期时间)。
如果你想了解更多关于 |
签名是使用编码后的标头、编码后的载荷、一个密钥以及标头中指定的算法计算出来的。无论你在标头部分选择哪种算法,你都必须使用该算法来加密 JWT
的前两部分:base64(header) + '.' + base64(payload)
,例如,在下面的伪代码中:
// 签名算法
data = base64urlEncode(header) + '.' + base64urlEncode(payload)
hashedData = hash(data, secret)
signature = base64urlEncode(hashedData)
签名是 JWT
中唯一不公开可读的部分,因为它使用密钥进行加密。除非有人拥有该密钥,否则他们无法解密此信息。因此,上述伪代码的示例输出是三个由点分隔的 Base64-URL
字符串,可以轻松地在 HTTP
请求中传递:
// JWT 令牌
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4Zj
ItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
让我们在下一节通过一个示例流程来看看这种令牌身份验证是如何工作的。
令牌认证流程
基于令牌的身份验证可以通过以下示例身份验证流程来理解:
-
用户从其浏览器上的客户端应用向服务器发送其凭据,例如用户名和密码。
-
如果凭据正确,服务器会检查用户名和密码,并返回一个签名的令牌(
JWT
)。 -
此令牌存储在客户端。它可以存储在本地存储、会话存储或
Cookie
中。 -
客户端应用通常将此令牌作为附加标头包含在后续对服务器的任何请求中。
-
服务器接收并解码
JWT
,如果令牌有效,则允许请求访问。 -
当用户注销时,客户端会销毁令牌,并且不再需要与服务器进行进一步交互。
在基于令牌的身份验证中,通常你不应该在载荷中包含任何敏感信息,并且令牌不应长期保存。用于包含令牌的附加标头应采用以下格式:
Authorization: Bearer <token>
基于令牌的身份验证的可伸缩性不是问题,因为令牌存储在客户端。跨域共享也不是问题,因为 JWT
是一个包含所有必要信息的字符串,包含在请求标头中,服务器会对客户端发出的每个请求进行检查。在 Node.js
应用中,我们可以使用其中一个 Node.js
模块,例如 jsonwebtoken
,来为我们生成令牌。让我们在下一节中看看如何使用这个 Node.js
模块。
使用 Node.js 模块处理 JWT
正如我们之前提到的,jsonwebtoken
可用于在服务器端生成 JWT
。你可以在以下简化的步骤中同步或异步地使用此模块:
-
通过
npm
安装jsonwebtoken
:$ npm i jsonwebtoken
-
在服务器端导入并签署令牌:
import jwt from 'jsonwebtoken' const token = jwt.sign({ name: 'john' }, 'secret', { expiresIn: '1h' })
-
异步验证来自客户端的令牌:
try { const verified = jwt.verify(token, 'secret') } catch(err) { // 处理错误 }
如果你想了解更多关于此模块的信息,请访问 https://github.com/brianloveswords/node-jws。
因此,你现在对基于会话和基于令牌的身份验证有了基本的了解,我们将指导你如何在同时使用 Koa
和 Nuxt
的服务器端和客户端应用中应用它们。在本章中,我们将使用基于令牌的身份验证在我们的应用中创建两个身份验证选项:本地身份验证和 Google OAuth
身份验证。本地身份验证是我们应用内部和本地验证用户的选项,而 Google OAuth
身份验证是我们使用 Google OAuth
验证用户的选项。因此,让我们在接下来的章节中找到答案!