在 Node.js 中使用 JWT 和 Cookies

2025-06-05

在 Node.js 中使用 JWT 和 Cookies

尽管 JWT 是一种非常流行的身份验证方法,并且受到许多人的喜爱,但大多数人最终还是将其存储在本地存储中。我无意在此争论在前端存储 JWT 的最佳方式,这并非我的本意。

如果你已经读过我写的关于如何使用 JWT 创建一个简单的身份验证和授权系统的文章,你一定注意到了,当从登录路由发出 http 请求时,我会将 JWT 作为响应发送。也就是说,我的想法是将其保存在本地存储中。

但是,还有其他方法可以将 jwt 发送到前端,今天我将教您如何将 jwt 存储在 cookie 中。

为什么要使用 Cookie?

有时候我有点懒,所以每次向 API 发出请求时,都不想在 headers 里不断发送 jwt。这时就需要用到 cookie 了,这样每次发出 http 请求时,你都可以放心地发送它们。

另一个原因是,如果你使用 localstorage,则在前端必须确保用户注销时 jwt 已从 localstorage 中删除。而使用 Cookie 时,你只需要在 API 中添加一个路由,发出 http 请求即可删除前端的 Cookie。

有几个原因促使人们选择使用 cookie,这里我给出了在制定项目过程中可能出现的一些简单的小例子。

现在我们已经有了大致的想法,让我们开始编码吧!

让我们开始编码

首先我们将安装以下依赖项:

npm install express jsonwebtoken cookie-parser
Enter fullscreen mode Exit fullscreen mode

现在只需创建一个简单的 Api:

const express = require("express");

const app = express();

app.get("/", (req, res) => {
  return res.json({ message: "Hello World 🇵🇹 🤘" });
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api up and running at: http://localhost:${port}`);
    });
  } catch (error) {
    console.error(error);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

您可能已经猜到了,我们需要一些东西来在我们的 Api 中处理 cookie,这就是cookie 解析器的用武之地。

首先,我们将导入它并将其注册到我们的中间件中。

const express = require("express");
const cookieParser = require("cookie-parser");

const app = express();

app.use(cookieParser());

//Hidden for simplicity
Enter fullscreen mode Exit fullscreen mode

现在我们准备开始在我们的 Api 中创建一些路由。

我们要创建的第一个路由是登录路由。首先,我们将创建 JWT,然后将其存储在名为“access_token”的 Cookie 中。该 Cookie 包含一些选项,例如 httpOnly(在应用程序开发阶段使用)和 Secure(在生产环境中使用,使用 https)。

然后我们会发送回复说我们已经成功登录。

app.get("/login", (req, res) => {
  const token = jwt.sign({ id: 7, role: "captain" }, "YOUR_SECRET_KEY");
  return res
    .cookie("access_token", token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
    })
    .status(200)
    .json({ message: "Logged in successfully 😊 👌" });
});
Enter fullscreen mode Exit fullscreen mode

现在登录完成后,让我们检查一下我们是否在客户端中收到了带有 jwt 的 cookie,在本例中我使用了Insomnia

登录

身份验证完成后,我们来执行授权。为此,我们必须创建一个中间件来检查是否有 cookie。

const authorization = (req, res, next) => {
  // Logic goes here
};
Enter fullscreen mode Exit fullscreen mode

现在我们必须检查我们是否有名为“access_token”的 cookie,如果没有,那么我们将禁止访问控制器。

const authorization = (req, res, next) => {
  const token = req.cookies.access_token;
  if (!token) {
    return res.sendStatus(403);
  }
  // Even more logic goes here
};
Enter fullscreen mode Exit fullscreen mode

如果我们有 Cookie,我们将验证令牌以获取数据。但是,如果发生错误,我们将禁止访问控制器。

const authorization = (req, res, next) => {
  const token = req.cookies.access_token;
  if (!token) {
    return res.sendStatus(403);
  }
  try {
    const data = jwt.verify(token, "YOUR_SECRET_KEY");
    // Almost done
  } catch {
    return res.sendStatus(403);
  }
};
Enter fullscreen mode Exit fullscreen mode

现在是时候在请求对象中声明新属性,以便我们更轻松地访问令牌的数据。

为此,我们将创建req.userId并赋值为令牌中的 ID。我们还将创建req.userRole并赋值为令牌中的角色。然后只需授予控制器访问权限即可。

const authorization = (req, res, next) => {
  const token = req.cookies.access_token;
  if (!token) {
    return res.sendStatus(403);
  }
  try {
    const data = jwt.verify(token, "YOUR_SECRET_KEY");
    req.userId = data.id;
    req.userRole = data.role;
    return next();
  } catch {
    return res.sendStatus(403);
  }
};
Enter fullscreen mode Exit fullscreen mode

现在我们要创建一个新的路由,这次我们将创建注销路由。基本上,我们将从 Cookie 中删除值。也就是说,我们将删除 JWT。

但是,我们想在新路由中添加授权中间件。这是因为我们希望在用户拥有 Cookie 时注销。如果用户拥有 Cookie,我们将删除其值并发送一条消息,告知用户已成功注销。

app.get("/logout", authorization, (req, res) => {
  return res
    .clearCookie("access_token")
    .status(200)
    .json({ message: "Successfully logged out 😏 🍀" });
});
Enter fullscreen mode Exit fullscreen mode

现在让我们测试一下是否可以注销。目的是验证第一次注销时,会收到一条消息,提示注销成功。但是,当我们再次测试时,如果没有 cookie,就会出现一条错误信息,提示禁止注销。

登出

现在我们只需要创建最后一个路由,以便从 JWT 中获取数据。只有当我们有权访问 Cookie 中的 JWT 时,才能访问此路由。否则,将会收到错误。现在,我们就可以使用添加到请求中的新属性

app.get("/protected", authorization, (req, res) => {
  return res.json({ user: { id: req.userId, role: req.userRole } });
});
Enter fullscreen mode Exit fullscreen mode

如果我们在我们最喜欢的客户端上测试它。我们将首先测试整个工作流程。遵循以下几点:

  • 登录获取cookie;
  • 访问受保护的路由,查看jwt数据;
  • 退出登录即可清除cookie;
  • 再次访问受保护的路线,但这次我们预计会出现错误。

我在这里留下一个 gif 来展示最终结果应该如何:

最终的

最终代码必须如下:

const express = require("express");
const cookieParser = require("cookie-parser");
const jwt = require("jsonwebtoken");

const app = express();

app.use(cookieParser());

const authorization = (req, res, next) => {
  const token = req.cookies.access_token;
  if (!token) {
    return res.sendStatus(403);
  }
  try {
    const data = jwt.verify(token, "YOUR_SECRET_KEY");
    req.userId = data.id;
    req.userRole = data.role;
    return next();
  } catch {
    return res.sendStatus(403);
  }
};

app.get("/", (req, res) => {
  return res.json({ message: "Hello World 🇵🇹 🤘" });
});

app.get("/login", (req, res) => {
  const token = jwt.sign({ id: 7, role: "captain" }, "YOUR_SECRET_KEY");
  return res
    .cookie("access_token", token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
    })
    .status(200)
    .json({ message: "Logged in successfully 😊 👌" });
});

app.get("/protected", authorization, (req, res) => {
  return res.json({ user: { id: req.userId, role: req.userRole } });
});

app.get("/logout", authorization, (req, res) => {
  return res
    .clearCookie("access_token")
    .status(200)
    .json({ message: "Successfully logged out 😏 🍀" });
});

const start = (port) => {
  try {
    app.listen(port, () => {
      console.log(`Api up and running at: http://localhost:${port}`);
    });
  } catch (error) {
    console.error(error);
    process.exit();
  }
};
start(3333);
Enter fullscreen mode Exit fullscreen mode

最后说明

显然,这个例子很简单,我强烈建议您阅读更多相关内容。但我希望我的分享能帮助您解答所有疑问。

你呢?

您是否使用过或读过有关此身份验证策略的信息?

文章来源:https://dev.to/franciscomendes10866/using-cookies-with-jwt-in-node-js-8fn
PREV
使用 Zustand 和 Immer 管理 React 应用的状态 让我们开始编码
NEXT
使用 React Hook Form 和 Yup 进行表单验证 让我们开始编码 你呢