发布于 2026-01-05 6 阅读
0

保持无状态 - Node JS (REST) 中的 JWT + Cookies DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

保持无状态 - Node.js (REST) 中的 JWT + Cookies

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

JWT 是无状态的。使用 cookie 作为容器来存储 JWT 既简单又可扩展。

为什么存储在 Cookie 中

之所以需要将 JWT 存储在 cookie 中,是因为需要考虑跨站脚本攻击 (XSS) 和跨站请求伪造 (CSRF) 之间的区别。
稍加研究即可了解详情,但简而言之:

  • XSS攻击是指攻击者通过在表单中​​输入脚本并检查浏览器是否会运行这些脚本,将恶意代码插入客户端浏览器。XSS利用了用户对特定网站的信任。
  • 另一方面,CSRF 攻击会强迫最终用户在已登录(即已建立会话)的 Web 应用程序上执行非预期操作。CSRF 攻击专门针对状态更改请求,而非数据窃取,因为攻击者无法查看伪造请求的响应。CSRF 利用了网站对用户浏览器的信任。
  • XSS攻击以多种方式发生。
  • CSRF 攻击只能在经过身份验证的用户会话被劫持时发生,攻击者代表用户执行操作。
  • 存储在浏览器本地存储中的无状态 JWT 更容易受到 XSS 攻击,而不太容易受到 CSRF 攻击。
  • 如果 Cookie 设置为 HTTPOnly 且 secure 标志设置为 true,则其受 XSS 攻击的可能性较小。但仅使用 HTTPOnly 可能无法完全阻止攻击,因为攻击者可能利用 XSS + HTTP Trace(跨站跟踪)来获取 Cookie。
  • Cookie 更容易受到 CSRF 攻击。然而,与种类繁多的 XSS 攻击相比,CSRF 攻击的缓解方法更为人熟知。

在Node.js中将JWT存储在cookie中

步骤 1 - 在注册或登录时创建 JWT

  • 安装 JWT 和 dotenv
 npm install jsonwebtoken
 npm install dotenv
Enter fullscreen mode Exit fullscreen mode
  • 生成令牌并将其保存在 Cookie 中的令牌中。
const jwt = require('jsonwebtoken');
//import jwt from 'jsonwebtoken'; 

const generateToken = (res, id, firstname) => {
  const expiration = process.env.DB_ENV === 'testing' ? 100 : 604800000;
  const token = jwt.sign({ id, firstname }, process.env.JWT_SECRET, {
    expiresIn: process.env.DB_ENV === 'testing' ? '1d' : '7d',
  });
  return res.cookie('token', token, {
    expires: new Date(Date.now() + expiration),
    secure: false, // set to true if your using https
    httpOnly: true,
  });
};
module.exports = generateToken

// generateToken.js file
Enter fullscreen mode Exit fullscreen mode

步骤 2 - 使用 Cookie 解析器

  • 安装 cookie 解析器和 cors
npm install cookie-parser
npm install cors

Enter fullscreen mode Exit fullscreen mode
  • 确保服务器使用 cookie 解析器


const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const loginRouter = require('./path/to/loginRouter');
require('dotenv').config();
const server = express();
server.use(express.json());
server.use(cors());
server.use(cookieParser());
// server should use login router
server.use('/', loginRouter)

const PORT = process.env.PORT || 5000;

server.listen(PORT, () => console.log(`Server started at port ${PORT}`));

// server.js file

Enter fullscreen mode Exit fullscreen mode

步骤 3 - 登录/注册时调用生成令牌

const generateToken = require('./path/to/generateToken');

const login = async (req, res) => {
  const { email, password } = req.body;

  try {
// get user details based on the login parameters

    const result = await emailExists(email);

    const { id, firstname} = result;

    await generateToken(res, id, firstname);
// carry out other actions after generating token like sending a response);
  } catch (err) {
    return res.status(500).json(err.toString());
  }
};
// loginController.js file

Enter fullscreen mode Exit fullscreen mode

验证存储在 Cookie 中的 JWT

要创建受保护的路由,用户需要经过身份验证。验证用户身份需要解密 cookie 中获取的令牌。为此,您需要一个身份验证中间件,该中间件从 cookie 中获取令牌,验证令牌是否仍然有效,并将验证操作传递给控制器​​。

const dotenv = require('dotenv');
const jwt = require('jsonwebtoken');

dotenv.config();
const verifyToken = async (req, res, next) => {
  const token = req.cookies.token || '';
  try {
    if (!token) {
      return res.status(401).json('You need to Login')
    }
    const decrypt = await jwt.verify(token, process.env.JWT_SECRET);
    req.user = {
      id: decrypt.id,
      firstname: decrypt.firstname,
    };
    next();
  } catch (err) {
    return res.status(500).json(err.toString());
  }
};

module.exports = verifyToken;

// verifyToken.js file
Enter fullscreen mode Exit fullscreen mode

然后,它会被用作中间件来验证用户身份。如果用户身份通过​​验证,则调用控制器;否则,向用户发送授权错误信息。

const express = require('express');
const user = require('./userController');
const verifyToken = require('../path/to/verifyToken');

const router = express.Router();

router.post(
  '/auth/verify',
  verifyToken,
  user.verifyMail,
);
)
module.exports = router;

// LoginRouter.js file

Enter fullscreen mode Exit fullscreen mode

使用后端生成的 Cookie 进行前端身份验证

当后端创建 cookie 时,如果 HTTPOnly 选项设置为 true,则前端无法访问这些 cookie。当向服务器发出请求时,这些 cookie 会嵌入到请求头中。

  • 在后端设置 CORS

在后端使用 Cookie 时,需要明确指定请求来源。为此,请确保服务器已启用 CORS,并将凭据设置为 true,且 origin 配置中包含所需的来源。一种简便的更改来源的方法是将前端配置保存为环境变量,并使用 process.env 进行设置。

// server.js (backend)
server.use(
  cors({
    origin: [
      `${process.env.FRONT_URL}`,
      'http://localhost:3000',
      'https://mypage.com',
    ],
    credentials: true
  })
);

Enter fullscreen mode Exit fullscreen mode
  • 确保将请求头随请求一起发送到后端。

在前端,当使用 axios 或 fetch 从后端发起请求时,请确保请求头随请求一起发送。对于 axios 调用,请将 `with credentials` 选项设置为 `true`,无论使用 `get` 还是 `post` 调用。如果使用 fetch 调用,也需要进行类似的设置。

// axios post
axios
    .post(`${url}`, {contentToBeSent}, {
      withCredentials: true
    })

// fetch get
fetch('https://example.com', {
  credentials: 'include'
});
Enter fullscreen mode Exit fullscreen mode

至于如何通过 JWT + Cookies 来提高安全性,就留给您自己去尝试吧。

欢迎随时通过basil_cea或以下帖子联系我。

文章来源:https://dev.to/mr_cea/remaining-stateless-jwt-cookies-in-node-js-3lle