用户Schema和密码加密
首先,我们使用Mongoose定义用户数据模型。这里包含用户名(username)和密码(password),并且在密码字段上设置了一个预保存钩子(pre-save hook),用于在存储到数据库前对其进行bcrypt加密,使用bcryptjs工具。
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// 定义用户Schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
// 其他字段...
});
// 预保存钩子:在保存用户前对密码进行bcrypt哈希处理
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10); // 加密强度为10的bcrypt算法
next();
});
// 创建并注册User Model
const User = mongoose.model('User', userSchema);
用户登录功能
创建一个处理用户登录请求的API端点,验证email和密码是否匹配,并在成功时生成JWT Token,如果用户不存在则创建一个新用户。
文件:routes/user.js
// usersRoutes.js
const express = require("express");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const secret = 'mySuperSecretKeyForJwtSigning';
const { User } = require("../model/main"); // 确保路径正确指向db.js文件
const router = express.Router();
// 定义登录路由
router.post("/login", async (req, res) => {
try {
const { email, password, name } = req.body;
// 先尝试查询数据库获取对应用户名的用户
let user = await User.findOne({ email });
if (!user) {
// 如果用户不存在,则创建新用户并保存到数据库
user = new User({ email, password, name });
await user.save();
} else {
// 对于已存在的用户,验证密码是否匹配
if (!await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
}
// 密码正确或新用户已创建,生成Token
const token = jwt.sign({ userId: user._id }, secret, { expiresIn: "1h" }); // 设置过期时间为1小时
// 将Token和其他用户信息返回给客户端
res.json({ token, name: user.name || null, id: user._id, email: user.email });
} catch (error) {
console.error(error);
res.status(500).json({ error: "Server error" });
}
});
// 查询单个用户或所有用户接口
router.get("/:id?", async (req, res) => {
try {
if (req.params.id) {
const userId = req.params.id;
const user = await User.findById(userId);
if (!user) {
return res.status(404).json({ message: "User not found." });
}
res.json(user);
} else {
const users = await User.find();
res.json(users);
}
} catch (error) {
console.error("Error fetching user(s):", error);
res
.status(500)
.json({ error: "An error occurred while fetching the user(s)." });
}
});
module.exports = router;
JWT Token生成说明
这里利用jsonwebtoken库根据用户ID生成一个签名的Token,这个Token内包含了用户的唯一标识符userId。设定一定的过期时间以提高安全性。
const jwt = require('jsonwebtoken');
const secret = 'mySuperSecretKeyForJwtSigning' // process.env.JWT_SECRET 这个secret应从环境变量中获取,确保安全
// 假设上述login函数内部
const token = jwt.sign({ userId: user._id }, secret, { expiresIn: '1h' }); // 过期时间为1小时
JWT鉴权中间件
创建一个中间件,该中间件会在每次保护路由被访问时执行。其作用是从请求头中提取Token,并解码验证Token的有效性。如果有效,则将解码后的用户信息附加到请求对象上以便后续操作。
文件:auth.js
const jwt = require("jsonwebtoken");
const secret = "mySuperSecretKeyForJwtSigning";
const authMiddleware = (req, res, next) => {
const noAuthPaths = ["/api/users/login"];
if (noAuthPaths.some((path) => req.path.startsWith(path))) {
return next(); // 路径在白名单内,直接进入下一个中间件或路由处理函数
}
const authorizationHeader = req.headers["authorization"];
// 检查请求头中是否存在Authorization头,并从中提取出Bearer后面的Token部分
if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "Unauthorized" });
}
const token = authorizationHeader.split(" ")[1];
try {
// 解码并验证Token
const decoded = jwt.verify(token, secret);
// 将解码得到的用户信息添加到请求对象上
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: "Unauthorized" });
}
};
module.exports = authMiddleware;
在应用中全局启用鉴权中间件
const authMiddleware = require('./auth');
app.use(authMiddleware)
server.js文件完整代码
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const { connect } = require('./db'); // 确保路径正确指向db.js文件
const authMiddleware = require('./auth');
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(authMiddleware)
// 在服务器启动前先连接数据库
async function startServer() {
try {
await connect();
console.log('Connected to MongoDB');
// 挂载用户路由到 /api/users
const usersRoutes = require('./routes/user');
app.use('/api/users', usersRoutes);
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
} catch (error) {
console.error('Error connecting to MongoDB:', error);
}
}
startServer();
前端调用展示:
以上代码展示了如何在Node.js与Mongoose配合下实现用户登录及基于JWT的Token鉴权机制的基本流程。实际项目中还应考虑更多细节,例如异常处理、Token刷新策略、Token黑名单管理等。