主题
使用 JWT 验证
JSON Web Token (JWT) 是一种开放标准(RFC 7519),用于在网络应用环境中以一种简短、安全的方式传递信息。JWT 在实现用户认证时比传统的基于 Session 的验证方式更加灵活,尤其适用于分布式架构、微服务等场景。在本章节中,我们将展示如何在 Express 中使用 JWT 进行用户认证。
1. 安装依赖
首先,我们需要安装 jsonwebtoken
库来生成和验证 JWT:
bash
npm install jsonwebtoken
我们还将使用 bcryptjs
来加密用户密码,提高系统的安全性:
bash
npm install bcryptjs
2. 配置 Express 项目
接着,创建一个基本的 Express 应用,并设置必要的中间件:
js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
const SECRET_KEY = 'your-secret-key'; // 用于加密和解密 JWT 的密钥
// 使用 body-parser 解析 POST 请求的表单数据
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 用户数据库模拟
const users = [
{ username: 'john_doe', password: bcrypt.hashSync('password123', 10) },
{ username: 'jane_doe', password: bcrypt.hashSync('12345', 10) }
];
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
在上面的代码中,bcrypt.hashSync
用于加密密码,这样即使用户密码暴露,数据也更为安全。
3. 用户注册与登录
3.1 用户注册
用户注册时,我们将密码通过 bcryptjs
进行加密,并将用户信息存储在我们的“数据库”中。
js
app.post('/register', (req, res) => {
const { username, password } = req.body;
// 检查用户名是否已经存在
const userExists = users.some(user => user.username === username);
if (userExists) {
return res.status(400).send('用户名已存在');
}
// 加密用户密码
const hashedPassword = bcrypt.hashSync(password, 10);
// 将新用户添加到 "数据库" 中
users.push({ username, password: hashedPassword });
res.send('注册成功');
});
3.2 用户登录
在用户登录时,我们首先验证用户名和密码,如果正确,我们将为用户生成一个 JWT 并返回给客户端,客户端可以在后续请求中通过 HTTP 请求头携带这个 JWT 进行身份验证。
js
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 查找匹配的用户
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).send('用户名或密码错误');
}
// 验证密码是否匹配
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) {
return res.status(401).send('用户名或密码错误');
}
// 生成 JWT
const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
});
在这段代码中,我们通过 jwt.sign()
生成一个包含用户名的 JWT,并设置了过期时间为 1 小时。生成的 JWT 将被返回给客户端。
4. 保护需要认证的路由
在需要用户认证的路由上,我们需要验证客户端传递的 JWT 是否有效。我们可以通过 Authorization
请求头获取 JWT,然后使用 jsonwebtoken
库进行解密和验证。
4.1 创建认证中间件
js
function authenticateToken(req, res, next) {
const token = req.header('Authorization') && req.header('Authorization').split(' ')[1]; // 获取请求头中的 token
if (!token) {
return res.status(403).send('访问被拒绝,未提供 token');
}
// 验证 token
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).send('无效的 token');
}
req.user = user; // 将解码后的用户信息附加到请求对象上
next(); // 继续执行后续的路由处理
});
}
在这个中间件中,我们首先检查请求头中是否包含 Authorization
字段,如果存在则获取并验证 JWT。如果验证成功,解码后的用户信息将被存储在 req.user
中,继续执行后续的路由处理;如果验证失败,则返回 403 错误。
4.2 保护路由
现在,我们可以保护需要认证的路由。只有在用户提供有效的 JWT 时,才能访问这些路由。
js
app.get('/dashboard', authenticateToken, (req, res) => {
res.send(`欢迎回来,${req.user.username}`);
});
在上面的代码中,/dashboard
路由需要用户认证才能访问。如果请求中没有有效的 JWT,authenticateToken
中间件将返回 403 错误。
5. 退出登录
使用 JWT 时,用户退出登录的操作相对简单。JWT 不会存储在服务器端,因此只需要客户端删除存储的 JWT 即可实现退出。
js
app.get('/logout', (req, res) => {
res.send('已退出登录');
});
客户端只需要删除浏览器中存储的 JWT(例如,在 localStorage
或 sessionStorage
中),即可实现登出。
6. 完整代码示例
以下是完整的 Express 应用代码,展示了如何使用 JWT 来实现用户认证、保护路由以及退出登录的功能:
js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
const SECRET_KEY = 'your-secret-key';
// 使用 body-parser 解析 POST 请求的表单数据
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 用户数据库模拟
const users = [
{ username: 'john_doe', password: bcrypt.hashSync('password123', 10) },
{ username: 'jane_doe', password: bcrypt.hashSync('12345', 10) }
];
// 用户注册路由
app.post('/register', (req, res) => {
const { username, password } = req.body;
const userExists = users.some(user => user.username === username);
if (userExists) {
return res.status(400).send('用户名已存在');
}
const hashedPassword = bcrypt.hashSync(password, 10);
users.push({ username, password: hashedPassword });
res.send('注册成功');
});
// 用户登录路由
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).send('用户名或密码错误');
}
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) {
return res.status(401).send('用户名或密码错误');
}
const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
});
// 认证中间件
function authenticateToken(req, res, next) {
const token = req.header('Authorization') && req.header('Authorization').split(' ')[1];
if (!token) {
return res.status(403).send('访问被拒绝,未提供 token');
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).send('无效的 token');
}
req.user = user;
next();
});
}
// 保护路由
app.get('/dashboard', authenticateToken, (req, res) => {
res.send(`欢迎回来,${req.user.username}`);
});
// 退出登录
app.get('/logout', (req, res) => {
res.send('已退出登录');
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
总结
本章节介绍了如何在 Express 中使用 JWT 实现用户认证:
- 用户注册与登录:通过
jsonwebtoken
库生成和验证 JWT,客户端存储 JWT 以便后续请求使用。 - 保护路由:使用中间件验证 JWT,确保只有持有有效 JWT 的用户才能访问受保护的路由。
- 退出登录:JWT 不存储在服务器端,客户端