在 Node.js 中实现用户认证与授权
在现代Web应用中,用户认证与授权是至关重要的一环。无论是一个简单的博客系统,还是一个复杂的电子商务平台,都需要确保用户的身份是可信的,同时根据用户的角色或权限提供不同的访问级别。本文将介绍如何在 Node.js 中实现基本的用户认证与授权,并附带示例代码为您提供参考。
一、基础概念
在谈论用户认证和授权之前,首先要了解这两个概念的区别:
- 用户认证(Authentication):验证用户的身份,确认用户是谁。这通常通过用户名和密码来实现。
- 用户授权(Authorization):确认用户是否有权限执行某个操作,决定用户可以做什么。
二、技术栈
在本篇文章中,我们将使用以下技术栈:
- Node.js:作为JavaScript的运行环境。
- Express.js:一个简洁的Web框架,用于构建Web服务器。
- MongoDB:一个NoSQL数据库,用于存储用户数据。
- Mongoose:MongoDB的对象建模工具,可以简化与数据库的交互。
- JWT(JSON Web Token):用于在网络应用中安全地传递信息。
三、环境搭建
1. 安装Node.js和相关依赖
首先确保你本地安装了 Node.js 和 npm。然后可以使用以下命令创建一个新的项目:
mkdir user-auth-example
cd user-auth-example
npm init -y
接下来安装所需的依赖项:
npm install express mongoose bcryptjs jsonwebtoken dotenv
2. 创建基本的目录结构
在项目根目录下,创建一个server.js
文件和一个名为models
的文件夹。
四、构建用户模型
在models
文件夹中创建一个User.js
文件,用于定义用户模型。
// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
// 密码加密
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
// 密码比较
userSchema.methods.comparePassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
module.exports = mongoose.model('User', userSchema);
在这个文件中,我们定义了一个用户模型,包括用户名和密码字段。在用户保存到数据库之前,密码会被加密。在比较密码时,我们使用bcrypt来处理。
五、搭建Express应用
在根的server.js
文件中,搭建基本的Express应用。
// server.js
const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 5000;
// 连接 MongoDB 数据库
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
// 用户注册接口
app.post('/api/register', async (req, res) => {
const { username, password } = req.body;
try {
const newUser = new (require('./models/User'))({ username, password });
await newUser.save();
res.status(201).send('User registered');
} catch (err) {
res.status(400).send(err.message);
}
});
// 用户登录接口
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
const User = require('./models/User');
const user = await User.findOne({ username });
if (!user || !(await user.comparePassword(password))) {
return res.status(401).send('Invalid credentials');
}
const token = require('jsonwebtoken').({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
// 启动服务器
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
在这个文件中,我们设置了两个API,一个用于用户注册,另一个用于用户登录。在用户成功登录后,我们会生成一个JWT,并将其发送回客户端。
六、用户授权
为了演示用户授权,我们将创建一个简单的中间件,用于检查用户是否已登录。
// middleware/auth.js
const jwt = require('jsonwebtoken');
const authenticate = (req, res, next) => {
const token = req.header('Authorization').replace('Bearer ', '');
if (!token) return res.status(401).send('Access denied. No token provided.');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (ex) {
res.status(400).send('Invalid token.');
}
};
module.exports = authenticate;
现在,我们可以在有需要进行用户权限控制的路由中使用这个中间件。
1. 受保护的路由示例
在server.js
中添加一个受保护的路由:
// 获取用户信息接口
.get('/api/profile', authenticate, (req, res) => {
res.send(`Hello, your user ID is: ${req.user.userId}`);
});
当用户登录后,可以使用生成的JWT访问/api/profile
路由,响应时服务器将返回用户ID。
七、总结
到现在为止,我们已经在Node.js中实现了一个基本的用户认证和授权体系。我们使用了Express框架、MongoDB数据库以及JWT进行身份验证和授权。通过这种方式,我们不仅能够确保用户身份的正确性,同时也能根据权限管理用户的访问。