使用Node.js构建用户认证与仪表盘的Web应用

发布:2024-09-23 10:45 阅读: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', { useNewUrlParsertrueuseUnifiedTopologytrue }) // 连接MongoDB
    .then(() => console.log('MongoDB连接成功')) // 连接成功消息
    .catch(err => console.log(err)); // 连接失败消息

// 中间件
app.use(express.urlencoded({ extendedfalse })); // 解析URL编码的请求体
app.use(express.static('public')); // 提供静态文件服务

// EJS
app.set('view engine''ejs'); // 设置模板引擎为EJS

// Express会话
app.use(session({
    secret'secret'// 会话秘钥
    resavetrue// 强制会话保存
    saveUninitializedtrue // 保存未初始化的会话
}));

// 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(nullfalse, { message'该邮箱未注册' }); // 返回错误信息
                }

                // 验证密码
                bcrypt.compare(password, user.password, (err, isMatch) => {
                    if (err) throw err; // 抛出错误
                    if (isMatch) {
                        return done(null, user); // 返回用户信息
                    } else {
                        return done(nullfalse, { 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: {
        typeString,
        requiredtrue // 姓名为必填项
    },
    email: {
        typeString,
        requiredtrue// 邮箱为必填项
        uniquetrue // 邮箱唯一
    },
    password: {
        typeString,
        requiredtrue // 密码为必填项
    },
    date: {
        typeDate,
        defaultDate.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'// 登录失败重定向到登录页面
        failureFlashtrue // 启用闪现消息
    })(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进行容器化,方便部署和管理。您可以根据需求进一步扩展功能和美化界面。