在 Node.js 中实现身份验证与授权
在现代Web应用中,身份验证和授权扮演着至关重要的角色。实现一个安全的身份验证和授权机制既能保护用户的数据安全,又能提高用户体验。在本文中,我们将使用 Node.js 来实现简单的身份验证与授权,特别是使用 JSON Web Token (JWT)。
什么是身份验证与授权?
- 身份验证是验证用户身份的过程,确保用户是他们所声称的那个人。
- 授权是在身份验证之后,确定用户对某些资源的访问权限。
接下来,我们将通过实现一个简单的用户注册、登录和基于JWT的授权系统来展示如何在 Node.js 中完成这些过程。
环境准备
在开始之前,确保你已经安装了 Node.js 和 npm。然后,你可以使用以下步骤来初始化一个新的 Node.js 项目:
mkdir node-auth-example
cd node-auth-example
npm init -y
接下来,安装所需的依赖项:
npm install express mongoose bcrypt jsonwebtoken dotenv
express
:我们将要使用的 web 框架。mongoose
:MongoDB ODM,用于处理 MongoDB 数据库。bcrypt
:用于 hashing 用户密码。jsonwebtoken
:用于生成和验证 JWT。dotenv
:用于加载环境变量。
创建基本的项目结构
创建一个 server.js
文件作为我们的应用入口,并创建一个 models
目录来存放用户模型。
touch server.js
mkdir models
touch models/User.js
定义用户模型
在 models/User.js
文件中,我们将定义用户模型。这里我们使用 Mongoose 来创建一个用户的Schema。
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
const User = mongoose.model('User', userSchema);
module.exports = User;
实现注册与登录功能
在 server.js
中,首先设置 Express 应用:
// server.js
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('./models/User');
require('dotenv').config();
const app = express();
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/auth-example', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
// 用户注册
app.post('/register', async (req, res) => {
const { username, password } = req.body;
// 检查用户是否已经存在
const existingUser = await User.findOne({ username });
(existingUser) {
return res.status(400).send('User already exists');
}
// 哈希密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建新用户
const user = new User({
username,
password: hashedPassword
});
await user.save();
res.send('User registered successfully');
});
// 用户登录
app.post('/login', async (req, res) => {
const { username, password } = req.body;
// 查找用户
const user = await User.findOne({ username });
if (!user) {
return res.status(400).send('Invalid credentials');
}
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(400).send('Invalid credentials');
}
// 生成JWT
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
实现授权中间件
接下来,我们需要一个中间件来验证每个请求中的 JWT,并确保用户已通过身份验证。
// middleware/auth.js
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.sendStatus(403);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
module.exports = authenticateToken;
保护路由
现在,我们可以使用我们创建的 authenticateToken
中间件来保护某些路由,仅允许经过身份验证的用户访问这些路径。
// 受保护的路由
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Welcome user with ID: ${req.user.id}`);
});
完整代码
确保你的 server.js
文件看起来像这样:
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('./models/User');
const authenticateToken = require('./middleware/auth');
require('dotenv').config();
const app = express();
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/auth-example', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
// 用户注册
app.post('/register', async (req, res) => {
const { username, password } = req.body;
const existingUser = await User.findOne({ username });
if (existingUser) {
return res.status(400).send('User already exists');
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ username, password: hashedPassword });
await user.save();
res.send('User registered successfully');
});
// 用户登录
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user) {
return res.status(400).send('Invalid credentials');
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(400).send('Invalid credentials');
}
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
// 受保护的路由
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Welcome user with ID: ${req.user.id}`);
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
运行应用
为了运行你的 Node.js 应用程序,确保设置了一个 JWT_SECRET
在 .env
文件中:
JWT_SECRET=yoursecretkey
然后,你可以运行你的应用程序:
node server.js
你可以使用 Postman 或任何其他 API 测试工具来测试注册、登录和访问受保护的路由。
结语
通过以上步骤,我们实现了一个基本的身份验证与授权机制。尽管这个例子相对简单,但它为你提供了一个良好的基础,你可以在此基础上构建更复杂的系统。在实际应用中,你可能还需要考虑其他安全性因素,比如加密存储、HTTPS、刷新 token 等等。