使用Node.js构建用户认证与仪表盘的Web应用
阅读:48
点赞:0
构建一个使用Node.js的Web应用可以实现高效且可扩展的平台。本文将指导您如何搭建一个简单的应用,包含用户认证和仪表盘界面。我们将使用MongoDB作为数据库,Express作为Web框架,EJS作为模板引擎,并使用Docker对应用进行容器化以便于部署。
一. 前置条件
在开始之前,请确保您的系统上已安装以下软件:
-
Node.js 和 npm -
Docker -
Docker Compose -
您喜欢的文本编辑器或IDE
二. 项目设置
1. 初始化新的Node.js项目
打开您的终端,并执行以下命令:
mkdir nodejs-app # 创建新目录
cd nodejs-app # 进入目录
npm init -y # 初始化Node.js项目
2. 安装依赖包
安装Express、EJS、Mongoose等必要的包:
npm install express ejs mongoose bcryptjs express-session passport passport-local
三. 创建应用结构
创建应用所需的目录和文件:
nodejs-app/
│
├── app.js # 主应用文件
├── Dockerfile # Docker配置文件
├── docker-compose.yml # Docker Compose配置文件
├── package.json # 项目配置文件
├── routes/ # 路由目录
│ ├── index.js # 首页路由
│ ├── user.js # 用户相关路由
├── config/ # 配置目录
│ ├── passportConfig.js # Passport配置文件
├── views/ # 视图目录
│ ├── login.ejs # 登录页面
│ ├── register.ejs # 注册页面
│ ├── dashboard.ejs # 仪表盘页面
│ ├── layout.ejs # 布局文件
│ └── partials/ # 部分视图目录
│ └── messages.ejs # 消息提示
├── models/ # 模型目录
│ ├── User.js # 用户模型
└── public/ # 静态文件目录
├── css/ # CSS样式文件
├── style.css # 样式文件
下面是一个Shell脚本(create-nodejs-app.sh),用于自动创建Node.js应用的目录结构:
#!/bin/bash
# 定义文件夹结构
mkdir -p nodejs-app/{routes,config,views,models,public/css}
# 创建必要的文件
touch nodejs-app/app.js
touch nodejs-app/Dockerfile
touch nodejs-app/docker-compose.yml
touch nodejs-app/package.json
touch nodejs-app/routes/index.js
touch nodejs-app/routes/user.js
touch nodejs-app/config/passportConfig.js
touch nodejs-app/views/login.ejs
touch nodejs-app/views/register.ejs
touch nodejs-app/views/dashboard.ejs
touch nodejs-app/views/layout.ejs
touch nodejs-app/views/partials/messages.ejs
touch nodejs-app/models/User.js
touch nodejs-app/public/css/style.css
# 确认消息
echo "Node.js应用目录结构创建成功!"
四. 开发后端
1. 设置Express和中间件
在app.js
中,设置Express应用和中间件以处理请求:
const express = require('express'); // 导入Express库
const mongoose = require('mongoose'); // 导入Mongoose库
const session = require('express-session'); // 导入会话管理库
const flash = require('connect-flash'); // 导入消息闪现库
const passport = require('passport'); // 导入Passport库
const app = express(); // 创建Express应用
const port = 3000; // 定义端口号
// Passport配置
require('./config/passportConfig')(passport); // 加载Passport配置
// 数据库配置
mongoose.connect('mongodb://mongo:27017/nodejs-app', { useNewUrlParser: true, useUnifiedTopology: true }) // 连接MongoDB
.then(() => console.log('MongoDB连接成功')) // 连接成功消息
.catch(err => console.log(err)); // 连接失败消息
// 中间件
app.use(express.urlencoded({ extended: false })); // 解析URL编码的请求体
app.use(express.static('public')); // 提供静态文件服务
// EJS
app.set('view engine', 'ejs'); // 设置模板引擎为EJS
// Express会话
app.use(session({
secret: 'secret', // 会话秘钥
resave: true, // 强制会话保存
saveUninitialized: true // 保存未初始化的会话
}));
// Passport中间件
app.use(passport.initialize());
app.use(passport.session());
// Connect Flash
app.use(flash());
// 全局变量
app.use((req, res, next) => {
res.locals.success_msg = req.flash('success_msg'); // 成功消息
res.locals.error_msg = req.flash('error_msg'); // 错误消息
res.locals.error = req.flash('error'); // 错误
next(); // 继续执行下一个中间件
});
// 路由
app.use('/', require('./routes/index')); // 加载首页路由
app.use('/users', require('./routes/user')); // 加载用户相关路由
// 启动服务器
app.listen(port, () => {
console.log(`服务器已在端口${port}上启动`); // 启动成功消息
});
2. 配置Passport进行用户认证
在config/passportConfig.js
中配置Passport:
const LocalStrategy = require('passport-local').Strategy; // 导入本地策略
const mongoose = require('mongoose'); // 导入Mongoose库
const bcrypt = require('bcryptjs'); // 导入bcrypt库
// 加载用户模型
const User = require('../models/User');
// Passport配置
module.exports = function (passport) {
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({ email: email }) // 查找用户
.then(user => {
if (!user) { // 如果用户不存在
return done(null, false, { message: '该邮箱未注册' }); // 返回错误信息
}
// 验证密码
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err; // 抛出错误
if (isMatch) {
return done(null, user); // 返回用户信息
} else {
return done(null, false, { message: '密码不正确' }); // 返回错误信息
}
});
})
.catch(err => console.log(err)); // 处理错误
}));
passport.serializeUser((user, done) => {
done(null, user.id); // 序列化用户ID
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user); // 反序列化用户
});
});
};
3. 用户模型
在models/User.js
中使用Mongoose定义用户模型:
const mongoose = require('mongoose'); // 导入Mongoose库
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true // 姓名为必填项
},
email: {
type: String,
required: true, // 邮箱为必填项
unique: true // 邮箱唯一
},
password: {
type: String,
required: true // 密码为必填项
},
date: {
type: Date,
default: Date.now // 默认日期为当前日期
}
});
// 创建用户模型
const User = mongoose.model('User', UserSchema);
module.exports = User; // 导出用户模型
4. 用户认证功能
在routes/user.js
中实现注册和登录功能:
const express = require('express'); // 导入Express库
const router = express.Router(); // 创建路由器
const bcrypt = require('bcryptjs'); // 导入bcrypt库
const passport = require('passport'); // 导入Passport库
const User = require('../models/User'); // 导入用户模型
// 注册页面
router.get('/register', (req, res) => res.render('register')); // 渲染注册页面
// 处理注册请求
router.post('/register', (req, res) => {
const { name, email, password, password2 } = req.body; // 获取请求数据
let errors = []; // 错误数组
// 验证输入
if (!name || !email || !password || !password2) {
errors.push({ msg: '请填写所有字段' }); // 添加错误信息
}
if (password !== password2) {
errors.push({ msg: '密码不匹配' }); // 添加错误信息
}
if (errors.length > 0) {
res.render('register', { errors, name, email, password,
password2 }); // 返回错误信息
} else {
User.findOne({ email: email }) // 查找用户
.then(user => {
if (user) {
errors.push({ msg: '该邮箱已被注册' }); // 添加错误信息
res.render('register', { errors, name, email, password, password2 }); // 返回错误信息
} else {
const newUser = new User({ // 创建新用户
name,
email,
password
});
// 哈希密码
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err; // 抛出错误
newUser.password = hash; // 保存哈希后的密码
newUser.save() // 保存用户
.then(user => {
req.flash('success_msg', '注册成功,可以登录了'); // 注册成功消息
res.redirect('/users/login'); // 重定向到登录页面
})
.catch(err => console.log(err)); // 处理错误
});
});
}
});
}
});
// 登录页面
router.get('/login', (req, res) => res.render('login')); // 渲染登录页面
// 处理登录请求
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/dashboard', // 登录成功重定向到仪表盘
failureRedirect: '/users/login', // 登录失败重定向到登录页面
failureFlash: true // 启用闪现消息
})(req, res, next);
});
// 仪表盘页面
router.get('/dashboard', (req, res) => {
res.render('dashboard', { user: req.user }); // 渲染仪表盘
});
// 退出登录
router.get('/logout', (req, res) => {
req.logout(); // 退出登录
req.flash('success_msg', '您已成功退出'); // 退出成功消息
res.redirect('/users/login'); // 重定向到登录页面
});
module.exports = router; // 导出路由器
5. 创建视图文件
1. 注册页面 (views/register.ejs
)
<%- include('layout.ejs') %>
<div class="container">
<h1>注册</h1>
<% if (errors.length > 0) { %>
<div class="alert alert-danger">
<ul>
<% errors.forEach(function(error) { %>
<li><%= error.msg %></li>
<% }); %>
</ul>
</div>
<% } %>
<form action="/users/register" method="POST">
<div class="form-group">
<label for="name">姓名</label>
<input type="text" name="name" class="form-control" value="<%= name %>">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" name="email" class="form-control" value="<%= email %>">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" name="password" class="form-control">
</div>
<div class="form-group">
<label for="password2">确认密码</label>
<input type="password" name="password2" class="form-control">
</div>
<button type="submit" class="btn btn-primary">注册</button>
</form>
</div>
2. 登录页面 (views/login.ejs
)
<%- include('layout.ejs') %>
<div class="container">
<h1>登录</h1>
<% if (error) { %>
<div class="alert alert-danger"><%= error %></div>
<% } %>
<form action="/users/login" method="POST">
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
3. 仪表盘页面 (views/dashboard.ejs
)
<%- include('layout.ejs') %>
<div class="container">
<h1>欢迎, <%= user.name %>!</h1>
<p>这是您的仪表盘</p>
<a href="/users/logout" class="btn btn-danger">退出</a>
</div>
五. Docker化应用
1. Dockerfile
在Dockerfile
中配置应用:
# 使用Node.js的官方镜像
FROM node:14
# 设置工作目录
WORKDIR /usr/src/app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm install
# 复制所有源代码
COPY . .
# 暴露应用的端口
EXPOSE 3000
# 启动应用
CMD ["node", "app.js"]
2. Docker Compose配置
在docker-compose.yml
中配置服务:
version: '3'
services:
mongo:
image: mongo:latest
ports:
- "27017:27017"
app:
build: .
ports:
- "3000:3000"
depends_on:
- mongo
environment:
- MONGO_URI=mongodb://mongo:27017/nodejs-app
六. 启动应用
在终端中运行以下命令以启动应用:
docker-compose up --build
访问 http://localhost:3000
,您将看到应用程序的首页。您可以注册新用户并登录到仪表盘。
七. 总结
在本文中,我们使用Node.js构建了一个简单的用户认证与仪表盘Web应用。应用使用了MongoDB存储用户信息,并利用Docker进行容器化,方便部署和管理。您可以根据需求进一步扩展功能和美化界面。