多人博客管理系统登录功能
多人博客管理系统
目标
- 能够知道搭建项目环境的步骤
- 能够理解模板优化
- 熟悉登录功能的思路逻辑
- 知道密码为什么需要加密
- 知道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 依赖的其他环境
- python 2.x
- node-gyp;npm install -g node-gyp
- 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})
})
多人博客管理系统登录功能相关推荐
- 博客项目——登录功能实现
客户端(login.art) 登录功能实现,表单要添加name属性,这样才能向服务器提交数据 <div class="login-body"><div class ...
- (黑马程序员)MongoDB + Express + art-template 项目实例-博客管理系统 第一页
1.项目环境搭建 1.1.项目介绍 多人博客管理系统: 1.博客内容展示 2.博客管理功能 1.2.项目初始化 1.建立项目所需文件夹: ● public 静态资源 ● model 数据库操作 ● r ...
- PHP+MySQL实现博客管理系统
DataBase 数据库课程设计 实验目标 PHP+MySQL实现博客管理系统 使用语言 后台-php 前端-html css js 相关知识可以在菜鸟教程学习,链接如下菜鸟教程 实验步骤 一. 环境 ...
- 个人博客管理系统_教程 | 一文搭建你的第一个免费专属博客
点击蓝字关注我 本文将详细介绍利用Github+hexo搭建一个免费.简洁的个人博客,从获取域名到菜单栏.搜索框.评论分享这些必要功能的配置,给自己一个个性化的内容分享平台. -▼- 我建了一个QQ学 ...
- 第1章 Express MongoDB 搭建多人博客
学习环境 Node.js : 0.10.22 + Express : 3.4.4 + MongoDB : 2.4.8 + 快速开始 安装 Express express 是 Node.js 上最流行的 ...
- nodejs实战《一起学 Node.js》 使用 Express + MongoDB 搭建多人博客
GitHub: https://github.com/nswbmw/N-blog N-blog 使用 Express + MongoDB 搭建多人博客 开发环境 Node.js: 6.9.1 Mong ...
- 个人博客/博客管理系统/Siteserver cms
本文介绍基于Siteserver(自助建站系统)实现的博客管理系统的原创实例,未套用现成站点模板,所有功能代码均手敲 目录 预览 介绍 安装教程 打开IIS 下载安装Siteserver cms 使用 ...
- SpringBoot的个人博客管理系统(毕业论文范文)
基于spring boot 的个人博客系统 摘要 随着社会上计算机行业的发展,记录知识的手段就不仅限于笔记,逐渐由纸质衍生到电子笔记,继而随着电子记录文档方式的多样性发展,大家将所学的知识进行回顾总 ...
- 计算机毕业设计系列基于SSM的个人博客管理系统
目录 一.项目介绍 二.开题报告 三.项目截图 四.源码获取 一.项目介绍 计算机毕业设计系列基于SSM的个人博客管理系统 本项目是一款基于SSM的个人博客管理系统.该个人博客管理系统基于B/S架构, ...
最新文章
- 2018-3-24论文(Grey Wolf Optimizer)note1----------No Free Lunch Theorem
- 轻松 [2007年4月22日]
- hashmap 判断key是否存在
- Java Web学习(二)数据加密
- 2019ICPC(沈阳) - Fish eating fruit(树形dp+树根转移)
- 创建线程后为什么马上调用CloseHandle()来关闭句柄
- 手持移动扫描终端 PDA移动开单系统-批发零售管理
- python数据预测模型算法_Python AI极简入门:4、使用回归模型预测房价
- IAR Embedded Workbench IDE 显示行号
- SpringCloudAlibaba-Nacos 介绍、单机模式下搭建及基本使用
- Power Spectral Density
- 计算机控制面板没,没有nvidia控制面板,手把手教你电脑没有nvidia控制面板
- 马哥2019python全套视频-马哥Python60集全套视频网盘链接免费分享
- 【安卓按键精灵】教你一个小时自己开发脚本,零基础1个小时上手
- 开发在线投票系统过程遇到的问题
- 人工智能基础——知识的表示方法,产生式表示法
- 网页的登录和注册页面
- multiple definition of `main'
- java_Stream流和Optional
- 下载Docker Compose超时的问题(Fail connect to github-production-release-asset-2e65be.s3.amazonaws.com:443)