保持无状态 - 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
- 生成令牌并将其保存在 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
步骤 2 - 使用 Cookie 解析器
- 安装 cookie 解析器和 cors
npm install cookie-parser
npm install cors
- 确保服务器使用 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
步骤 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
验证存储在 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
然后,它会被用作中间件来验证用户身份。如果用户身份通过验证,则调用控制器;否则,向用户发送授权错误信息。
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
使用后端生成的 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
})
);
- 确保将请求头随请求一起发送到后端。
在前端,当使用 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'
});
至于如何通过 JWT + Cookies 来提高安全性,就留给您自己去尝试吧。
欢迎随时通过basil_cea或以下帖子联系我。
文章来源:https://dev.to/mr_cea/remaining-stateless-jwt-cookies-in-node-js-3lle