多人博客管理系统

目标

  • 能够知道搭建项目环境的步骤
  • 能够理解模板优化
  • 熟悉登录功能的思路逻辑
  • 知道密码为什么需要加密
  • 知道cookie跟session是什么
  • 能够参照笔记写出登录功能

功能需求

  • 博客内容展示

    • 文章列表页面
      [外链图片转存失败(img-twPJASUX-1568462831538)(images/博客-01.png)]

    • 文章详情页面

      [外链图片转存失败(img-zKIf5sXe-1568462831539)(images/博客-02.png)]

  • 博客管理功能

    • 登录页面

      [外链图片转存失败(img-NcjieJDZ-1568462831544)(images/博客-03.png)]

    • 管理页面

      [外链图片转存失败(img-8wzKRKRq-1568462831546)(images/博客-04.png)]

项目环境搭建

  • 建立项目所需文件夹

    • public 静态资源
    • model 数据库操作
    • route 路由
    • views 模板
  • 初始化项目描述文件

    • npm init -y
  • 下载项目所需要的第三方模块

    • npm install express mongoose art-template express-art-template
  • 创建网站服务器

    //引入 express第三方模块
    const express = require('express');
    //创建web服务器
    const app = express();
    //监听端口
    app.listen(80);
    
  • 构建模块化路由

    • 在 route 文件夹下 创建 admin.js 和 home.js 两个模块的路由文件

    • admin.js

      // 引用expess框架
      const express = require('express');
      // 创建博客展示页面路由
      const admin = express.Router();
      // 将路由对象做为模块成员进行导出
      module.exports = admin;
      
    • home.js

      // 引用expess框架
      const express = require('express');
      // 创建博客展示页面路由
      const home = express.Router();
      // 将路由对象做为模块成员进行导出
      module.exports = home;
      
    • 在入口文件 app.js文件中配置模块化路由

      // 引入路由模块
      const home = require('./route/home');
      const admin = require('./route/admin');
      // 为路由匹配请求路径
      app.use('/home', home);
      app.use('/admin', admin);
      
  • 配置静态资源文件

    • 引入path文件,利用static方法来配置静态资源

      // 处理路径
      const path = require('path');
      // 开放静态资源文件
      app.use(express.static(path.join(__dirname, 'public')))
      
    • 把静态资源的html文件,放到views目录下

  • 构建模板

    // 告诉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')))
    

    注意 : 模板中的相对路径是相对于地址栏中的请求路径的

    所以在模板中引用其他文件中的资源的时候 需要使用服务器的绝对路径 也就是在src 或者 link的路径前加上“/”

    <link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/admin/css/base.css">
    

模板优化

抽离公共部分到单独模板中去

在common文件夹中创建头部公共模板文件

header.art

<!-- 头部 -->
<div class="header"><!-- 网站标志 --><div class="logo fl">黑马程序员 <i>ITHEIMA</i></div><!-- /网站标志 --><!-- 用户信息 --><div class="info"><div class="profile dropdown fr"><span class="btn dropdown-toggle" data-toggle="dropdown">{{userInfo && userInfo.username}}<span class="caret"></span></span><ul class="dropdown-menu"><li><a href="user-edit.html">个人资料</a></li><li><a href="/admin/logout">退出登录</a></li></ul></div></div><!-- /用户信息 -->
</div>
<!-- /头部 -->

创建aside.art

<!-- 侧边栏 -->
<div class="aside fl"><ul class="menu list-unstyled"><li><a class="item active" href="user.html"><span class="glyphicon glyphicon-user"></span>用户管理</a></li><li><a class="item" href="article.html"><span class="glyphicon glyphicon-th-list"></span>文章管理</a></li></ul><div class="cprt">Powered by <a href="http://www.itheima.com/" target="_blank">黑马程序员</a></div>
</div>
<!-- 侧边栏 -->

在use.art中引入

<body><!-- 头部 --><!-- 子模板的相对路径相对的就是当前文件 因为它是由模板引擎解析的 而不是浏览器 -->{{include './common/header.art'}}<!-- /头部 --><!-- 主体内容 --><div class="content"><!-- 侧边栏 -->{{include './common/aside.art'}}<!-- 侧边栏 -->...</div>
</body>

抽取layout.art 骨架模板,每个页面可能还需要引入其他样式或者是javascript代码,所以我们需要在骨架模板中设置坑,后续每个页面想添加了通过 block 引入进来就可以了

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><title>Blog - Content Manager</title><link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css"><link rel="stylesheet" href="/admin/css/base.css"><!-- CSS的坑 -->{{block 'link'}}{{/block}}
</head><body><!-- 页面主体的坑 -->{{block 'main'}} {{/block}}<script src="/admin/lib/jquery/dist/jquery.min.js"></script><script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script><script src="/admin/js/common.js"></script><!-- javascript的坑 -->{{block 'script'}} {{/block}}
</body></html>

后续页面需要通过 extend来进行引入,这里用一个 user.art进行实例,后续页面参考即可

<!-- 引入骨架模板 -->
{{extend './common/layout.art'}}
<!-- 告诉骨架模板,需要在哪个地方插入内容 需要插入的内容要包含在 {{block 'main'}} {{/block}} 里面 -->
{{block 'main'}} <!-- 子模板的相对路径相对的就是当前文件 因为它是由模板引擎解析的 而不是浏览器 -->{{include './common/header.art'}}<!-- 主体内容 --><div class="content">{{include './common/aside.art'}}...</div><!-- /主体内容 --><!-- 删除确认弹出框 --><div class="modal fade confirm-modal">...</div>
{{/block}}

登录功能

用户的数据存储在数据库中,所以我们需要去链接数据库,创建用户的集合

连接数据库

model文件夹下,创建connect.js 文件,里面写连接数据库代码

// 引入mongoose第三方模块
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost/blog', {useNewUrlParser: true }).then(() => console.log('数据库连接成功')).catch(() => console.log('数据库连接失败'))

创建用户集合

model文件夹下,创建user.js 文件,里面创建用户规则

// 创建用户集合
// 引入mongoose第三方模块
const mongoose = require('mongoose');
// 创建用户集合规则
const userSchema = new mongoose.Schema({username: {type: String,required: true,minlength: 2,maxlength: 20},email: {type: String,// 保证邮箱地址在插入数据库时不重复unique: true,required: true},password: {type: String,required: true},// admin 超级管理员// normal 普通用户role: {type: String,required: true},// 0 启用状态// 1 禁用状态state: {type: Number,default: 0}
});// 创建集合
const User = mongoose.model('User', userSchema);// 将用户集合做为模块成员进行导出,
//module.exports = {//  User:User
//}
//在es6中,如果属性名跟属性值都相等,可以简写
module.exports = {User
}

初始化一些数据存入数据库

为了方便我们实现登录功能我们先初始化一个用户;这段代码用来测试 执行过一次 创建过admin之后就应该删除或者注释掉

User.create({username:'itheima',email:'itheima@itcast.cn',password:'123456', role:'admin',state:0
}).then(()=>{console.log('用户创建成功')
}).catch(()=>{console.log('用户创建失败')
})

为登录表单设置相应属性

需要设置请求地址,请求方式,添加name属性

<!-- 请求路径为/admin/login 请求方式为 post -->
<form action="/admin/login" method="post" id="loginForm"><div class="form-group"><label>邮件</label><!-- 添加name属性,不然提交的话值不会传递到服务器 --><input name="email" type="email" class="form-control" placeholder="请输入邮件地址"></div><div class="form-group"><label>密码</label><input name="password" type="password" class="form-control" placeholder="请输入密码"></div><button type="submit" class="btn btn-primary">登录</button>
</form>

客户端验证用户是否填写了登录表单

<script src="/admin/js/common.js"></script>
<script type="text/javascript">// 为表单添加提交事件 默认是true$('#loginForm').on('submit', function () {// 获取到表单中用户输入的内容  $().serializeToJson 是jQuery提供的方法,返回的值是一个数组,如果用户在邮箱里面填写了 itheima@itcast.cn 那么获取到的值: [{'email':'itheima@itcast.cn'}]var result = serializeToJson($(this))// 如果用户没有输入邮件地址的话if (result.email.trim().length == 0) {alert('请输入邮件地址');// 阻止程序向下执行 不会进行提交return false;}// 如果用户没有输入密码if (result.password.trim().length == 0) {alert('请输入密码')// 阻止程序向下执行 不会进行提交return false;}});</script>

上面实例代码的 serializeToJson() 是自己封装的一个函数,这个属于是一个公共的代码,所以放在了 public/admin/js/common.js

function serializeToJson(form) {var result = {};// [{name: 'email', value: '用户输入的内容'}]var f =  form.serializeArray();  f.forEach(function (item) {// result.emailresult[item.name] = item.value;});return result;
}

服务器端添加登录路由

post请求我们需要依赖 body-parser 模块,利用 npm install body-parser 下载

在app.js 里面引入 body-parser模块,绑定路由

// 引入body-parser模块 用来处理post请求参数
const bodyPaser = require('body-parser');
// 处理post请求参数
app.use(bodyPaser.urlencoded({ extended: false }));

服务器这边还需要对账号密码进行校验

// 添加实现登录请求的路由
admin.post('/login', (req,res)=>{ // 接收请求参数const {email, password} = req.body;// 如果用户没有输入邮件地址// if (email.trim().length == 0 || password.trim().length == 0) return res.status(400).send('<h4>邮件地址或者密码错误</h4>');if (email.trim().length == 0 || password.trim().length == 0) return    res.status(400).render('admin/error', {msg: '邮件地址或者密码错误'});//});

代码到这一步还只完成了校验是否为空,后面需要通过用户名跟密码与数据库中进行比对

//用户集合定义在user.js中,我们需要进行导入
const { User } = require('../model/user')admin.post('/login', async (req,res)=>{ // 接收请求参数const {email, password} = req.body;...//数据库的查询是异步的,所以我们可以通过 async 配合 await来得到查询的结果let user = await User.findOne({email});// 查询到了用户if (user) {// 将客户端传递过来的密码和用户信息中的密码进行比对// true 比对成功// false 对比失败// 如果密码比对成功if ( password = user.password ) {// 登录成功res.send('登录成功');} else {// 没有查询到用户res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'})}} else {// 没有查询到用户res.status(400).render('admin/error', {msg: '邮箱地址或者密码错误'})}});

密码加密-哈希密码加密

账号的密码是比较私密的,如果是明文进行传递,那么很容易被别人盗取,所以我们需要对密码进行一个加密处理,这里我们使用一个第三方的模块 bcrypt

bcrypt 依赖的其他环境

  1. python 2.x
  2. node-gyp;npm install -g node-gyp
  3. windows-build-tools;npm install --global --production windows-build-tools

实例demo

// 导入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();

把加密处理引入到我们项目中

在保存到数据库之前,需要把密码进行加密,需要在user.js里面去处理

async function createUser() {const salt = await bcrypt.genSalt(10);const pass = await bcrypt.hash('123456', salt);const user = await User.create({username: 'iteheima',email: 'itheima@itcast.cn',password: pass,role: 'admin',state: 0});
}

由于我们把数据库中的密码进行了加密,所以我们不能直接用用户输入的密码与数据库的密码比对,需要把用户传递过来的密码进行加密,然后再和数据库中的比对

...
if (user) {console.log(password);// 将客户端传递过来的密码和用户信息中的密码进行比对// true 比对成功// false 对比失败let isValid = await bcrypt.compare(password, user.password);// 如果密码比对成功if (isValid) {// 登录成功// 将用户名存储在请求对象中req.username = user.username;// 重定向到用户列表页面res.redirect('/admin/user');} else {// 没有查询到用户res.status(400).render('admin/error', { msg: '邮箱地址或者密码错误' })}}...

登录状态的保持-cookie&session

cookie:浏览器在电脑硬盘中开辟的一块空间,主要供服务器端存储数据。

  • cookie中的数据是以域名的形式进行区分的。
  • cookie中的数据是有过期时间的,超过时间数据会被浏览器自动删除。
  • cookie中的数据会随着请求被自动发送到服务器端。

[外链图片转存失败(img-CWaxCiy9-1568462831548)(images/博客-05.png)]

session:实际上就是一个对象,存储在服务器端的内存中,在session对象中也可以存储多条数据,每一条数据都有一个sessionid做为唯一标识

[外链图片转存失败(img-lYSh0sUg-1568462831551)(images/博客-06.png)]

实现session功能需要借助 express-session

语法:

//首先需要下载 express-session: npm install express-session
const session = require('express-session');
app.use(session({ secret: 'secret key' }));

app.js 中引入

// 导入express-session模块
const session = require('express-session');
// 配置session
app.use(session({secret: 'secret key',saveUninitialized: false,cookie: {maxAge: 24 * 60 * 60 * 1000}
}));

login的路由中,当用户登录成功后,需要把用户名保存在session中

...
if (user) {console.log(password);// 将客户端传递过来的密码和用户信息中的密码进行比对// true 比对成功// false 对比失败let isValid = await bcrypt.compare(password, user.password);// 如果密码比对成功if (isValid) {// 登录成功// 将用户名存储在请求对象中,当我们引入了express-session模块后,我们可以通过req对象得到session对象req.session.username = user.username;// 重定向到用户列表页面res.redirect('/admin/user');} else {// 没有查询到用户res.status(400).render('admin/error', { msg: '邮箱地址或者密码错误' })}}...

在用户页面从session中获取数据

// 创建用户列表路由
admin.get('/user',(req,res)=>{res.render('admin/user',{msg:req.session.username})
})

多人博客管理系统登录功能相关推荐

  1. 博客项目——登录功能实现

    客户端(login.art) 登录功能实现,表单要添加name属性,这样才能向服务器提交数据 <div class="login-body"><div class ...

  2. (黑马程序员)MongoDB + Express + art-template 项目实例-博客管理系统 第一页

    1.项目环境搭建 1.1.项目介绍 多人博客管理系统: 1.博客内容展示 2.博客管理功能 1.2.项目初始化 1.建立项目所需文件夹: ● public 静态资源 ● model 数据库操作 ● r ...

  3. PHP+MySQL实现博客管理系统

    DataBase 数据库课程设计 实验目标 PHP+MySQL实现博客管理系统 使用语言 后台-php 前端-html css js 相关知识可以在菜鸟教程学习,链接如下菜鸟教程 实验步骤 一. 环境 ...

  4. 个人博客管理系统_教程 | 一文搭建你的第一个免费专属博客

    点击蓝字关注我 本文将详细介绍利用Github+hexo搭建一个免费.简洁的个人博客,从获取域名到菜单栏.搜索框.评论分享这些必要功能的配置,给自己一个个性化的内容分享平台. -▼- 我建了一个QQ学 ...

  5. 第1章 Express MongoDB 搭建多人博客

    学习环境 Node.js : 0.10.22 + Express : 3.4.4 + MongoDB : 2.4.8 + 快速开始 安装 Express express 是 Node.js 上最流行的 ...

  6. nodejs实战《一起学 Node.js》 使用 Express + MongoDB 搭建多人博客

    GitHub: https://github.com/nswbmw/N-blog N-blog 使用 Express + MongoDB 搭建多人博客 开发环境 Node.js: 6.9.1 Mong ...

  7. 个人博客/博客管理系统/Siteserver cms

    本文介绍基于Siteserver(自助建站系统)实现的博客管理系统的原创实例,未套用现成站点模板,所有功能代码均手敲 目录 预览 介绍 安装教程 打开IIS 下载安装Siteserver cms 使用 ...

  8. SpringBoot的个人博客管理系统(毕业论文范文)

     基于spring boot 的个人博客系统 摘要 随着社会上计算机行业的发展,记录知识的手段就不仅限于笔记,逐渐由纸质衍生到电子笔记,继而随着电子记录文档方式的多样性发展,大家将所学的知识进行回顾总 ...

  9. 计算机毕业设计系列基于SSM的个人博客管理系统

    目录 一.项目介绍 二.开题报告 三.项目截图 四.源码获取 一.项目介绍 计算机毕业设计系列基于SSM的个人博客管理系统 本项目是一款基于SSM的个人博客管理系统.该个人博客管理系统基于B/S架构, ...

最新文章

  1. 2018-3-24论文(Grey Wolf Optimizer)note1----------No Free Lunch Theorem
  2. 轻松 [2007年4月22日]
  3. hashmap 判断key是否存在
  4. Java Web学习(二)数据加密
  5. 2019ICPC(沈阳) - Fish eating fruit(树形dp+树根转移)
  6. 创建线程后为什么马上调用CloseHandle()来关闭句柄
  7. 手持移动扫描终端 PDA移动开单系统-批发零售管理
  8. python数据预测模型算法_Python AI极简入门:4、使用回归模型预测房价
  9. IAR Embedded Workbench IDE 显示行号
  10. SpringCloudAlibaba-Nacos 介绍、单机模式下搭建及基本使用
  11. Power Spectral Density
  12. 计算机控制面板没,没有nvidia控制面板,手把手教你电脑没有nvidia控制面板
  13. 马哥2019python全套视频-马哥Python60集全套视频网盘链接免费分享
  14. 【安卓按键精灵】教你一个小时自己开发脚本,零基础1个小时上手
  15. 开发在线投票系统过程遇到的问题
  16. 人工智能基础——知识的表示方法,产生式表示法
  17. 网页的登录和注册页面
  18. multiple definition of `main'
  19. java_Stream流和Optional
  20. 下载Docker Compose超时的问题(Fail connect to github-production-release-asset-2e65be.s3.amazonaws.com:443)

热门文章

  1. 方差分析的数学原理与公式讲解
  2. windows搭建nexus私服
  3. 自媒体短视频有哪些软件制作,自媒体短视频用什么工具
  4. JFreeChart在Struts2中实现3D柱状图统计
  5. 计算机毕业设计java+springboot宠物商城系统
  6. 视差特效的原理和实现方法
  7. HTML 和 CSS 笔记
  8. idea快速清理无效类文件
  9. 社区APP运营步骤:获取用户需要几步走?
  10. 南大通用GBase数据库为城轨交通正常运行保驾护航