如何在 Express.js 中轻松处理 JWT 身份验证

53 阅读5分钟

身份验证曾经让我感到压力很大。

这并不是因为它在概念上很难,而是因为我发现的每个教程要么将其过度简化到无用的程度,要么使其变得如此复杂,我需要博士学位才​​能理解发生了什么。

在构建了几个项目并以创造性的方式破解身份验证之后,我终于找到了一种真正有效的设置,并且不会让我想把笔记本电脑扔出窗外。

以一种好的方式感到惊讶

这是我的方法,解释起来就像我在和朋友喝咖啡聊天一样,而不是在写教科书。

我们正在做什么 Node.js 和 Express - 后端

MongoDB 和 Mongoose - 用户数据存储的地方

bcryptjs - 使密码无法读取(重要!)

jsonwebtoken - 创建每个人都谈论的那些令牌

cookie-parser - 正确处理 cookies

dotenv - 真正保守秘密

没什么特别的。没有什么是你不能在 30 秒内安装的。

第一步:让人们注册 当有人注册时,我们需要保存他们的信息。但我们不能就这样保存他们的密码。这就像把你家的钥匙放在门垫下,还贴了个“钥匙在这里”的标签一样。

const bcrypt = require('bcryptjs'); const User = require('../models/User');

app.post('/api/register', async (req, res) => { const { username, email, password } = req.body;

// Turn the password into scrambled nonsense const hashedPassword = await bcrypt.hash(password, 10);

const user = new User({ username, email, password: hashedPassword });

try { await user.save(); res.status(201).json({ message: 'User created successfully' }); } catch (err) { res.status(400).json({ error: 'User already exists or invalid input' }); } }); 发生了什么:我们获取他们的密码并将其加密(这就是哈希部分)。即使有人闯入了你的数据库,他们看到的也只是乱码,而不是真正的密码。

黑客入侵你的数据库后:

黑客入侵数据库后的反应

步骤 2:登录并获取令牌 这就是有趣的地方。当有人登录时,我们会给他们两个令牌:

访问令牌- 短暂有效(15分钟)。类似于进入某个地方的临时通行证。

刷新令牌- 有效期较长(5 天)。用于无需再次登录即可获取新的访问令牌。

为什么是两个?因为安全。如果有人窃取了你的访问令牌,它会很快过期。而刷新令牌则安全地保存在 Cookie 中。

const jwt = require('jsonwebtoken');

const generateAccessToken = (payload) => { return jwt.sign(payload, process.env.JWT_SECRET, {expiresIn: '15m'}) }

app.post('/api/login', async (req, res) => { const { email, password } = req.body;

// Find the user const user = await User.findOne({ email }); if (!user) return res.status(401).json({ error: 'Invalid credentials' });

// Check if password matches const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) return res.status(401).json({ error: 'Invalid credentials' });

// Package up user info const payload = { id: user._id, username: user.username, email: user.email }

// Create both tokens const refreshToken = jwt.sign( payload, process.env.JWT_REFRESH_SECRET, { expiresIn: '5d' } );

const accessToken = generateAccessToken(payload);

// Send refresh token as a secure cookie res.cookie("refreshToken", refreshToken, { secure: true, httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days })

res.status(200).json({ accessToken }); }); 发生了什么:我们检查用户是否存在,验证其密码,然后创建两个令牌。刷新令牌放在一个 JavaScript 无法触及的特殊 Cookie 中(这是其中的httpOnly一部分)。访问令牌放在响应中。

步骤 3:获取新的访问令牌 15 分钟后,您的访问令牌将过期。我们无需用户重新登录,而是使用刷新令牌来获取新的访问令牌。

首先,在主文件中设置 cookie 解析:

const cookieParser = require("cookie-parser"); app.use(cookieParser()); 然后创建刷新端点:

app.post('/token/refresh', (req, res) => { // Get the refresh token from the cookie const refreshToken = req.cookies.refreshToken; if (!refreshToken) return res.status(401).json({ error: 'No token' });

// Verify it's legit const verifiedToken = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); if (!verifiedToken) return res.status(403).json({ error: 'Invalid token' });

// Create a new access token const {id, username, email} = verifiedToken; const accessToken = generateAccessToken({id, username, email});

res.status(200).json({ accessToken }); }); 发生了什么:我们从 Cookie 中获取刷新令牌,检查它是否有效,如果有效,我们就创建一个新的访问令牌。很简单。

GIF 检查刷新访问令牌 meme

步骤 4:保护路线 现在我们需要确保只有登录用户才能访问某些路由。这就是中间件的作用。

const authMiddleware = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) return res.status(401).json({ error: 'No token provided' });

// Token comes as "Bearer actualtoken", so we split it const token = authHeader.split(' ')[1];

try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; // Now we know who's making the request next(); } catch (err) { res.status(403).json({ error: 'Invalid or expired token' }); } };

// Use it on any route you want to protect app.get('/api/profile', authMiddleware, async (req, res) => { const user = await User.findById(req.user.id).select('-password'); res.json(user); }); 发生了什么:中间件检查是否存在有效令牌。如果是,则将用户信息附加到请求中并继续处理。如果不是,则拒绝。可以将其视为检查 ID 的保镖。

为什么这个设置有效 它是无状态的。无需在服务器上存储会话。一切都存在于令牌中。

可扩展。无论您有 10 个用户还是 10,000 个用户,它都能正常工作。

它足够安全。密码经过哈希处理,令牌会过期,并且刷新令牌受到保护。

它并不难理解。即使没有计算机科学学位,你也可以理解每个部分的作用。

GIF 朴实的工作

接下来我要添加的内容 目前,这可以处理基本身份验证。但对于更大的项目,你可能需要:

基于角色的访问- 管理员可以比普通用户做更多的事情

令牌黑名单- 用户注销时使令牌无效

速率限制- 阻止人们向您的登录端点发送垃圾邮件

但对于大多数项目来说,这个设置是可靠的。

实话实说 身份验证不必太可怕或复杂。没错,确实存在一些极端情况。没错,确实有更安全的方法。但这种方法在我的项目中很有效,而且非常简单,我可以在一小时内完成设置,无需费心。

如果您刚开始使用后端身份验证,或者因为太复杂而一直拖延,那就从这里开始吧。先让它运行起来,了解每个部分的功能,然后再进行优化。

完美是发货的敌人。

有问题吗?想尝试不同的方法吗?请在评论区留言。我很乐意学习更好的方法。作者www.mjsyxx.com