JSON Web Tokens (JWT) 是一种用来传递认证信息的开放标准 (RFC 7519). 它可以通过加密和签名进行验证,并且可以在身份认证和授权过程中安全地传输信息。 在 Hapi.js 中实现 JWT 身份认证可以提供更高的安全性和更好的扩展性。
在本文中,我们将讨论使用 Hapi.js 和 JWT 实现身份认证时可能遇到的常见问题,以及如何解决这些问题,以及如何实现一个简单的 Hapi.js 应用程序,使用 JWT 进行身份验证。
JWT 基础
在 JWT 中,认证信息存储在 JSON 对象中,并使用一个密钥进行签名。签名允许应用程序验证请求的身份,并确保没有篡改过该信息。JWT可以通过将签名附加到消息中来发行,然后在访问路由时,可以通过验证签名来识别和验证请求。
以下是一个基本 JWT,由开发者指定:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT 由三个部分组成: 头部 (header)、载荷 (payload) 和签名 (signature)。
头部 (header)
头部包含有关令牌的元数据信息,如其类型(通常为 JWT)和所使用的加密算法。
{ "alg": "HS256", "typ": "JWT" }
alg 是用于签署JWT的算法,可以是HMAC SHA256,RSA等。typ表示令牌类型。
载荷 (payload)
载荷是JWT的有效数据和声明信息。载荷中包含有关用户的任何信息,以及应用程序所需的其他信息。作为一个约定,JWT载荷不加密,但是可以签名。
{ "sub": "1234567890", "name": "John Doe", "admin": true }
sub 是 JWT 的主题,name 是用户的名称,admin 是用户是否为管理员。
签名 (Signature)
JWT 使用密钥对组合的头部和载荷进行签名,以便验证它们是否完好无损,并以不可变的方式存储有关认证信息的所有信息。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
签名通过将base64UrlEncode的header和payload串联起来,在其之间放置一个点 (.) 并使用密钥对其进行哈希运算获得。解析 JWT 时,验证密钥和哈希之间是否存在匹配的信息。
使用 Hapi.js 和 JWT 实现身份认证
现在,我们已经了解了 JWT 的基础知识,我们将开始使用 Hapi.js 来设置 JWT 身份验证。
安装相关依赖
要在 Hapi.js 中使用 JWT,您需要安装以下依赖项:
npm install hapi joi jsonwebtoken hapi-auth-jwt2
hapi是一个 Node.js 的 HTTP 框架,用于构建 Web 应用程序、 RESTful API、服务等,常常用来构建企业级 Web 框架。
joi 是一个用于自描述数据校验的 JavaScript 库,它令数据校验变得更加容易。
jsonwebtoken 是一个用于 JSON 查询和生成JWT的 JavaScript 库。
hapi-auth-jwt2 是一个 hapi.js 插件,它实现了 JWT 验证并自动获取和验证JWT(以及从jwt中提取信息)的方法。
准备工作
首先,我们需要在 Hapi.js 项目中添加身份验证插件 hapi-auth-jwt2,并为了演示方便,我们在实现本例之前可以新建一个文件夹,并初始化 hapi 项目:
mkdir jwt-authentication cd jwt-authentication npm init -y npm install hapi --save
接下来,使用以下命令安装 hapi-auth-jwt2:
npm install hapi-auth-jwt2 --save
然后,使用以下命令在当前工作目录下创建一个名为 index.js 的文件。
touch index.js
打开 index.js,然后添加以下代码:
-- -------------------- ---- ------- ----- ---- - --------------------- ----- ---- - ----- -- -- - ----- ------ - ------------- ----- ----- ----- ----------- -- ----- -------------- ------------------- ------- -- ---- ---------------- - -------------------------------- ----- -- - ---------------- --------------- -- ------
这段代码创建并启动了 Hapi.js 服务器,它将侦听端口3000。如果打开 http://localhost:3000,您将看到回显信息。现在,我们将向服务器添加身份验证插件。
修改 index.js 的内容:
-- -------------------- ---- ------- ----- ---- - --------------------- ----- --- - -------------------- ----- ---- - --------------------- ----- --- - ----------------------- ----- ------------ - ------------------------- ----- ---- - ----- -- -- - ----- ------ - ------------- ----- ----- ----- ----------- -- ----- ----------------------------- --------------------------- ------ - ---- -------------- --------- ------------- -------------- - ----------- --------- - -- -------------------------- -------------- - ------- ------- ----- --------- -------- - ----- ------ --------- - -------- ------------ ------ -------------------------------- --------- ---------------------------------------------------- -- - -- -------- ----- --------- -- -- - ----- ---- - -------------- -- ------- --- ------------------------- -- ------- - ------ ------------------- --- -------- - -- -------------- --- ------------------------- - ------ ---------------------------- ----------- - ----- ----- - ---------- ------ ----------- ----- --------- -- -------------- - ---------- -------- ---------- ---- -- ------ - ----- - - -- - ------- ------ ----- ------ -------- ----- --------- -- -- - ------ - ------------ ------------------------ - - - -- ----- -------------- ------------------- ------- -- ---- ---------------- - ----- ------------ - ----- --------- -------- -- -- - -- ---------------- - ------ - -------- ----- - - ------ - -------- ---- - - ----- ----- - - - ------ ------------------ --------- ----------- ----- ----- ---- -- - ------ ------------------ --------- ----------- ----- ----- ---- - - -------------------------------- ----- -- - ---------------- --------------- -- ------
代码中使用了一些新的库,让我们逐一说明。
HapiAuthJwt2
HapiAuthJwt2 是用于 JWT 验证的 hapi.js 插件。通过验证 JWT,可以使用此插件从请求中提取数据。
以下代码段初始化 JWT 身份验证插件。
-- -------------------- ---- ------- ----- ----------------------------- --------------------------- ------ - ---- -------------- --------- ------------- -------------- - ----------- --------- - -- --------------------------
首先,我们在 Hapi.js 服务器上注册了 HapiAuthJwt2 插件。然后,我们定义了一个 JWT 身份验证策略,并将其设置为默认身份验证策略。
我们为身份验证策略提供了以下信息:
- 密钥:用于签名和验证令牌的密钥。
- 验证:用来验证令牌。 validate 方法用于在验证令牌之前用传递 的荷载解码令牌。如果验证不成功,则验证响应中的错误包含一个简单的错误消息。 validateUser 是授予或拒绝访问令牌的函数。
- 验证选项:有关 JWT 的验证选项。
Joi
使用 Joi 验证字段,确保客户端向服务器发送有效数据。这样可以防止对服务器执行恶意操作,以及在向数据库中写入数据之前对其进行验证,以提高其安全性。
validate: { payload: Joi.object({ email: Joi.string().email().required(), password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/).required() }) }
在这个例子中,使用了一个简单的类似正则表达式的模式来匹配密码字符串。
payload 关键字用于检查请求有效负载中提供的对象是否有效,并且必须具有以下值:
- email:必须是字符串,使用电子邮件模式进行验证。
- password:必须是字符串,并使用模式进行验证。
这些规则确保有效负载是有效的,并提高了服务器的安全性。
Boom
在这个示例中,我们使用了 Boom 库,它是另一个 hapi.js 生态系统的库,用于关于错误的编程。
return Boom.unauthorized('Incorrect password!')
例如,我们可以通过返回 “Boom.unauthorized” 来实现错误的用户身份验证消息。
jsonwebtoken
jsonwebtoken 库用于创建和验证 JSON Web Tokens(JWTs)。
const token = jwt.sign({ email: user.email, name: user.name }, 'mySecretKey', { algorithm: 'HS256', expiresIn: '1h' })
在此代码片段中,我们使用此库创建一个新 JWT 之前,增加可选的有效期为60分钟,这将添加到签名有效载荷中。
在上面代码的最后,服务器添加了两个路由。
-- -------------------- ---- ------- - ------- ------- ----- --------- -------- - ----- ------ --------- - -------- ------------ ------ -------------------------------- --------- ---------------------------------------------------- -- - -- -------- ----- --------- -- -- - ----- ---- - -------------- -- ------- --- ------------------------- -- ------- - ------ ------------------- --- -------- - -- -------------- --- ------------------------- - ------ ---------------------------- ----------- - ----- ----- - ---------- ------ ----------- ----- --------- -- -------------- - ---------- -------- ---------- ---- -- ------ - ----- - - -- - ------- ------ ----- ------ -------- ----- --------- -- -- - ------ - ------------ ------------------------ - - -
/login
我们使用路由接收登录信息,并验证用户。在这里,我们检查该用户是否存在,然后检查密码是否与哈希值匹配。如果匹配,则生成JWT,否则返回错误消息。
/me
在这个小例子中,我们查看验证过程是否工作。我们想看到最终的JWT解码后的有效负载。
在以下输出中,对示例用户 John Doe 进行身份验证并获取JWT以供下一次请求使用。运行 node index.js 后,打开浏览器,并打开“http://localhost:3000/login”。为了展示出现错误,我们输入了一个不正确的密码和一个不存在的电子邮件地址。
http://localhost:3000/login: { statusCode: 401, error: 'Unauthorized', message: 'Incorrect password!' } http://localhost:3000/login: { token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG5kb2VAZm9vLmNvbSIsIm5hbWUiOiJKb2huIERvZSJ9.Lo4b-kC9IkrydGh-iELofv2QFCcTfb_oHwGTPb_EY_U' } http://localhost:3000/me: { credentials: { email: 'johndoe@app.com', name: 'John Doe' } }
结论
本文介绍了如何在 Hapi.js 应用程序中设置 JWT 身份验证。我们讨论了使用 Hapi.js 和 JWT 时可能遇到的常见问题,以及如何解决这些问题。我们深入了解了如何实施 Hapi.js 应用程序,使用 JWT 进行身份验证,并提供了代码片段来说明基本操作。
在实际生产环境中,可以使用官方推荐的方法实现身份认证,例如 OAuth2 和 OpenID Connect 等。JWT 仅是身份验证的一种补充手段。
希望本文能够帮助您解决一些身份认证中的问题,并为您提供有关使用 JWT 在 Hapi.js 中实施身份认证的清晰指导。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/672ef012eedcc8a97c8b8245