Fastify 和 JWT 的整合及其用法

前言

随着前端技术的不断发展,越来越多的应用程序开始采用分布式系统架构来提高质量和可用性。JWT 作为一种轻量、快速且可靠的认证机制,已经广泛应用于这些分布式应用中。而 Fastify 作为一个基于 Node.js 平台的高效开发工具,正依托其优秀的性能和可扩展性,逐步成为前端领域的人气工具之一。那么,在这篇文章中,我们将探讨 Fastify 和 JWT 的整合及其使用,并提供一些可供参考的示例代码。

什么是 Fastify?

Fastify 是基于 Node.js 平台的一个高效、低延迟的 Web 框架。它同时具备了优秀的性能和可扩展性,因此,在 Node.js 社区中受到了极高的评价。

特点:

  • 快速:Fastify 在请求和响应处理方面极其快速,它体积小、效率高、代码简单易于维护。

  • 可扩展:开发人员可以轻松地通过插件和中间件来扩展 Fastify 的功能。

  • 现代化:Fastify 采用了 ES2017 的语法,并支持异步、await、promise 等语法特性,因此代码更加易读、简明。

什么是 JWT?

JWT(JSON Web Token)是一种用于认证和授权的标准化 JSON 编码的 token,它由三部分组成:header、payload 和 signature。

  • header 包含了 token 的类型以及所使用的加密算法。

  • payload 包含了包含用户信息(例如用户 ID、用户类型等)的 JSON 对象。

  • signature 是将 header 和 payload 连接在一起并使用密钥进行加密后的结果。

JWT 不需要在服务器端保存任何信息,因此它可以轻松地跨服务器或跨平台使用。

如何整合 Fastify 和 JWT?

安装依赖包

在使用 Fastify 和 JWT 之前,我们需要先通过 npm 安装相应的依赖包:

npm i fastify fastify-jwt

使用 JWT 插件

在 Fastify 中,我们可以通过引入 fastify-jwt 插件来实现 JWT 的功能。这一插件提供了 JWT 在 Fastify 中的实现,并提供了相关的 API 和钩子函数。

在 app.js 中添加以下代码来启用 JWT 插件:

const fastify = require('fastify')()

fastify
  .register(require('fastify-jwt'), { secret: 'supersecret' })

在这里,我们通过调用 fastify-jwt 模块来启用 JWT 插件,并指定密钥为 'supersecret'。

添加路由

为了使用 JWT 来保护我们的路由,我们需要在路由配置中使用一些中间件。如下所示:

fastify.route({
  method: 'GET',
  url: '/protected',
  preHandler: fastify.auth([fastify.verifyJWT]),
  handler: (request, reply) => {
    reply.send({ msg: 'Hello, JWT!' })
  }
})

这里我们定义了一个受保护的路由 '/protected',并使用了 fastify.auth() 函数来配置中间件。这里我们使用了 fastify-jwt 插件中提供的 fastify.verifyJWT 中间件来验证 JWT。如果验证成功,代码就会向客户端发送 'Hello, JWT!'。

我们也可以为不同的路由配置不同的中间件,如下所示:

fastify.route({
  method: 'GET',
  url: '/protected',
  preHandler: [
    fastify.auth([fastify.verifyJWT]),
    async (req, reply, done) => {
       // ... add your async logic here ...
       done() // pass control to the next preHandler
    },
  ],
  handler: (request, reply) => {
    reply.send({ msg: 'Hello, JWT!' })
  }
})

在这里,我们为 '/protected' 路由配置了两个中间件,其中一个是 fastify-jwt 提供的中间件,另一个则是一个 Promise/A+ 模型的中间件函数(async 函数)。这个 Promise 函数会在 fastify-jwt 中间件之后执行,因此可以在这里对请求进行一些异步处理。

如果您需要配置不受保护的路由,则可以像这样设置:

fastify.route({
  method: 'GET',
  url: '/',
  handler: (request, reply) => {
    reply.send({ msg: 'Hello, World!' })
  }
})

注册用户及发行 token

为了使用 JWT,我们需要为每个用户提供一个唯一的 token。在 Fastify 中,我们可以使用 fastify-jwt 插件提供的 sign() 方法来创建 token。例如:

fastify.post('/login', async (request, reply) => {
  const { username, password } = request.body
  const user = await User.findOne({ username })

  if (!user) {
    throw new Error('User not found')
  }

  if (password !== user.password) {
    throw new Error('Password incorrect')
  }

  const token = fastify.jwt.sign({ username })

  reply.send({ token })
})

在这里,我们使用了 post 方法来处理用户的登录请求。我们根据用户名从数据库中查询对应的用户信息。如果用户不存在或者密码错误,我们就直接抛出错误。

然后,我们使用 fastify.jwt.sign() 来签发一个 token,其中包含了用户的用户名信息。

最后,我们将 token 作为响应体中的一部分返回给客户端。

验证 token

在 Fastify 中,我们可以使用 verifyJWT() 中间件来验证请求的 token。例如:

fastify.route({
  method: 'GET',
  url: '/protected',
  preHandler: fastify.auth([fastify.verifyJWT]),
  handler: (request, reply) => {
    const username = request.user.username
    reply.send({ msg: `Hello, ${username}!` })
  }
})

在这里,我们使用 fastify.auth() 中间件来指定 verifyJWT() 中间件作为 preHandler,以确保只有验证通过的 token 才能访问该路由。fastify-jwt 插件会自动将验证通过的 token 信息存储在 request.user 对象中,并以此来验证对该路由的访问。

总结

通过本文的阐述,您应该已经了解了如何在 Fastify 中使用 JWT。其中,我们在配置路由和中间件时使用了 fastify.verifyJWT 中间件来验证 token 的有效性,同时使用了 fastify-jwt 插件提供的功能来签发和解密 token。希望本文能够为您在实际开发中的工作提供一定的指导和参考意义。

示例代码

const fastify = require('fastify')()
const mongoose = require('mongoose')
mongoose.connect(process.env.MONGODB_URI)

const User = require('./models/User')

fastify
  .register(require('fastify-jwt'), { secret: 'supersecret' })

fastify.route({
  method: 'GET',
  url: '/',
  handler: (request, reply) => {
    reply.send({ msg: 'Hello, World!' })
  }
})

fastify.route({
  method: 'GET',
  url: '/protected',
  preHandler: fastify.auth([fastify.verifyJWT]),
  handler: (request, reply) => {
    const username = request.user.username
    reply.send({ msg: `Hello, ${username}!` })
  }
})

fastify.post('/login', async (request, reply) => {
  const { username, password } = request.body
  const user = await User.findOne({ username })

  if (!user) {
    throw new Error('User not found')
  }

  if (password !== user.password) {
    throw new Error('Password incorrect')
  }

  const token = fastify.jwt.sign({ username })

  reply.send({ token })
})

fastify.listen(process.env.PORT || 3000, (err, address) => {
  if (err) throw err
  console.log(`Server listening on ${address}`)
})

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65aa71a1add4f0e0ff40bc0c


纠错反馈