多人博客后台管理DAY02

(二)项目功能的实现——用户管理

BLOG -- 源码目录 └── middleware 中间件 └──loginGuard.js 登录拦截 └── model -- 数据库操作 ├── connect.js --数据库连接 └── user.js --用户管理 └── public -- 静态资源 └── admin --博客管理页面静态资源 └── common.js --客户端验证登录表单 └──route -- 路由 └──admin --博客管理页面 ├──loginPage.js 登录页面渲染 ├──logout.js 退出页面功能实现 ├──userPage.js 用户页面渲染 └── login.js 登录页面功能实现└── admin.js --博客管理页面路由 └── views -- 模板 └── admin --博客管理页面art模板 └── common 公共模块 └── header.art 公共头部模块 ├── login.art --登录页面 └── error.art --错误页面 ├── app.js -- 创建网站服务 └──hash.js -- 对bcrypt的测试代码

1、connect.js
  • 链接数据库: 引入mongoose、链接数据库
// 引入mongoose第三方模块 const mongoose = require('mongoose'); // 导入config模块 const config = require('config'); console.log(config.get('db.host')) // 连接数据库 mongoose.connect(`mongodb://${config.get('db.user')}:${config.get('db.pwd')}@${config.get('db.host')}:${config.get('db.port')}/${config.get('db.name')}`, {useNewUrlParser: true }) .then(() => console.log('数据库连接成功')) .catch(() => console.log('数据库连接失败'))

2.app.js
  • 数据库链接:require connect
  • require('./model/user'); ·// 创建用户,建完之后需要注释掉
  • 服务器端接收请求参数,验证用户是否填写了登录表单:
    • 下载好第三方模块后重新启动服务器 npm install body-parser
    • app.js 引入body-parser模块 用来处理post请求参数
  • 显示登录状态,需要保存登录的状态:导入express-session模块,npm install express-session拦截请求 判断用户登录状态,
  • 拦截请求 判断用户登录状态,require loginGuard
// 引用expess框架 const express = require('express'); // 处理路径 const path = require('path'); // 引入body-parser模块 用来处理post请求参数 const bodyPaser = require('body-parser'); // 导入express-session模块 const session = require('express-session'); // 创建网站服务器 const app = express(); // 数据库连接 require('./model/connect'); // 处理post请求参数 app.use(bodyPaser.urlencoded({extended: false})); // 告诉express框架模板所在的位置 app.set('views', path.join(__dirname, 'views')); // 告诉express框架模板的默认后缀是什么 app.set('view engine', 'art'); // 当渲染后缀为art的模板时 所使用的模板引擎是什么 app.engine('art', require('express-art-template')); // 开放静态资源文件 app.use(express.static(path.join(__dirname, 'public'))); // 引入路由模块 const home = require('./route/home'); const admin = require('./route/admin'); // 拦截请求 判断用户登录状态 app.use('/admin', require('./middleware/loginGuard')); // 为路由匹配请求路径 app.use('/home', home); app.use('/admin', admin); // 监听端口 app.listen(80); console.log('网站服务器启动成功, 请访问localhost')

3.user.js
  • 创建用户集合,初始化用户:集合创建后记得删除,否则会重复创建报错,将用户集合做为模块成员进行导出
  • 使用了bcrypt加密方式,导入bcrypt模块,
// 创建用户集合 // 引入mongoose第三方模块 const mongoose = require('mongoose'); // 导入bcrypt const bcrypt = require('bcrypt'); // 创建用户集合规则 const userSchema = new mongoose.Schema({ username: { ··· }, email: { ··· } ··· }); // 创建集合 async function createUser () { const salt = await bcrypt.genSalt(10); const pass = await bcrypt.hash('123456', salt); const user = await User.create({ username: 'xx', email: 'xx', password: pass, role: 'admin', state: 0 }); } // createUser(); // 将用户集合做为模块成员进行导出 module.exports = { User }

【多人博客后台管理DAY02】4.login.art
  • 为登录表单项设置请求地址、请求方式以及表单项name属性,为方便获取表单,为表单添加id值
  • 如果邮件和密码其中一项没有输入,阻止表单提交
··· ···

5.common.js
  • 当用户点击登录按钮时,客户端验证用户是否填写了登录表单,并添加到layout.js中,使得每个页面都可以访问到
function serializeToJson(form) { var result = {}; // [{name: 'email', value: '用户输入的内容'}] var f =form.serializeArray(); f.forEach(function (item) { // result.email result[item.name] = item.value; }); return result; }

layout.js
··· {{block 'link'}}{{/block}} {{block 'main'}} {{/block}} ··· {{block 'script'}} {{/block}}

6.login.js
  • 实现登录功能,如果其中一项没有输入(trim().length == 0),为客户端做出响应,阻止程序向下执行
  • 根据邮箱地址查询用户信息(导入用户集合构造函数),如果用户不存在(用findOne查询),为客户端做出响应,阻止程序向下执行
  • 如果用户存在,将用户名和密码(使用了bcrypt加密)进行比对(bcrypt.compare(password, user.password)),比对成功,用户登录成功,比对失败,用户登录失败
    • 密码加密处理 bcrypt
    bcrypt依赖的其他环境
    1.安装python 并把python设置到环境变量中
    2.node-gyp
    npm install node-gyp -g
    3.windows-build-tools
    Python 2版本:npm install --global --production windows-build-tools
    Python其它版本:https://blog.csdn.net/weixin_...
  • 对用户角色进行判断, 将用户角色存储在session对象中,如果不是admin至登录页面,如果是则进入用户列表页面
  • req.app.locals,req可以获得app对象,选择其中的locals属性,可以不用多次渲染
  • render('admin/error')跳转失败则渲染error页面
// 导入用户集合构造函数 const { User } = require('../../model/user'); const bcrypt = require('bcrypt'); module.exports = async (req, res) => { // 接收请求参数 const {email, password} = req.body; // 如果用户没有输入邮件地址 // if (email.trim().length == 0 || password.trim().length == 0) return res.status(400).send('邮件地址或者密码错误'); if (email.trim().length == 0 || password.trim().length == 0) return res.status(400).render('admin/error', {msg: '邮件地址或者密码错误'}); // 根据邮箱地址查询用户信息 // 如果查询到了用户 user变量的值是对象类型 对象中存储的是用户信息 // 如果没有查询到用户 user变量为空 let user = await User.findOne({email}); // 查询到了用户 if (user) { // 将客户端传递过来的密码和用户信息中的密码进行比对 // true 比对成功 // false 对比失败 let isValid = await bcrypt.compare(password, user.password); // 如果密码比对成功 if ( isValid ) { // 登录成功 // 将用户名存储在请求对象中 req.session.username = user.username; // 将用户角色存储在session对象中 req.session.role = user.role; // res.send('登录成功'); req.app.locals.userInfo = user; // 对用户的角色进行判断 if (user.role == 'admin') { // 重定向到用户列表页面 res.redirect('/admin/user'); } else { // 重定向到博客首页 res.redirect('/home/'); }} else { // 没有查询到用户 res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'}) } } else { // 没有查询到用户 res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'}) } }

7.header.art
  • {{userInfo && userInfo.username}}判断userInfo是否存在,防止因为不存在而出现的报错
{{userInfo && userInfo.username}}

8.loginGuard.js
  • 实现登录拦截的功能:判断除了login以外的页面且已经属于登录状态(req.url != '/login' && !req.session.username)再判断用户属性是普通用户还是管理员
onst guard = (req, res, next) => { // 判断用户访问的是否是登录页面 // 判断用户的登录状态 // 如果用户是登录的 将请求放行 // 如果用户不是登录的 将请求重定向到登录页面 if (req.url != '/login' && !req.session.username) { res.redirect('/admin/login'); } else { // 如果用户是登录状态 并且是一个普通用户 if (req.session.role == 'normal') { // 让它跳转到博客首页 阻止程序向下执行 return res.redirect('/home/') } // 用户是登录状态 将请求放行 next(); } }module.exports = guard;

9.hash.js
// 导入bcrypt const bcrypt = require('bcrypt'); async function run () { // 生成随机字符串 // genSalt方法接收一个数值作为参数 // 数值越大 生成的随机字符串复杂度越高 // 数值越小 生成的随机字符串复杂度越低 // 默认值是 10 // 返回生成的随机字符串 const salt = await bcrypt.genSalt(10); // 对密码进行加密 // 1. 要进行加密的明文 // 2. 随机字符串 // 返回值是加密后的密码 const result = await bcrypt.hash('123456', salt); console.log(salt); console.log(result); }run();

10.loginPage.js
  • 模块分离功能:实现登录页面的渲染
  • 通过模块化来实现代码的复用,最后要把路由对象作为模块成员进行导出
  • 类似的还有logout.js userPage.js login.js
module.exports = (req, res) => { res.render('admin/login'); }

11.admin.js
  • 通过require把到处去的模块成员接入
  • admin同样作为模块成员进行导出,让app.js接入
// 引用expess框架 const express = require('express'); // 创建博客展示页面路由 const admin = express.Router(); // 渲染登录页面 admin.get('/login', require('./admin/loginPage')); // 实现登录功能 admin.post('/login', require('./admin/login')); // 创建用户列表路由 admin.get('/user', require('./admin/userPage')); // 实现退出功能 admin.get('/logout', require('./admin/logout')); // 将路由对象做为模块成员进行导出 module.exports = admin;

12、error.art
  • 优化页面:错误页面三秒后返回登录页面,基于layout骨架
{{extend './common/layout.art'}}{{block 'main'}}{{msg}}
{{/block}}{{block 'script'}}{{/block}}

    推荐阅读