准备
技术栈
node,express, nodemon,bcrypt,joi
1.注册
(1)添加注册路由及注册页面展示
//用户注册页面
router.get('/admin/register', (req, res) => {
const {
message
} = req.query
res.render("admin/user-edit.html", {
message
})
})
(2)视图层设置form提交数据,服务端得到数据
<form class="form-container" method="post" action="/admin/register">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" class="form-control" placeholder="请输入用户名">
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" name="email" class="form-control" placeholder="请输入邮箱地址">
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" class="form-control" placeholder="请输入密码">
</div>
<div class="form-group">
<label>角色</label>
<select class="form-control" name="role">
<option value="normal">普通用户</option>
<option value="admin">超级管理员</option>
</select>
</div>
<div class="form-group">
<label>状态</label>
<select class="form-control" name="state">
<option value="0">启用</option>
<option value="1">禁用</option>
</select>
</div>
<div class="buttons">
<input type="submit" class="btn btn-primary">
</div>
</form>
(3)视图层验证。 视图层验证表单内容是否填写
//1.表单序列化
function serializeToJson(form) {
var result={}
var f = form.serializeArray()
f.forEach(function (item) {
result[item.name] = item.value
})
return result
}
//2.判断表单字段
var result = serializeToJson(Form)//序列化获取表单
if (result.email.trim().length == 0) {
alert("请输入邮箱")
// 阻止表单默认提交
return false
}
(4)服务端验证。
- 验证表单内容是否符合格式 使用第三方模块 joi
const schema = joi.object({
username: joi.string().min(3).max(20).required().error(new Error("用户名设置错误")),
password: joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required().error(new Error("密码设置错误")),
email: joi.string().required().email({
minDomainSegments: 2,
tlds: {
allow: ['com', 'net']
}
}),
role: joi.string().valid('normal', 'admin').required().error(new Error("角色值设置错误")),
state: joi.number().valid(0, 1).required().error(new Error("状态值设置错误")),
})
try { //验证通过执行
// 实现验证
await schema.validateAsync(req.body)
} catch (e) { // 验证错误执行
//重定向到编辑页,并将错误信息传过去
return res.redirect(`/admin/register?message=${e.message}`)
}
- 验证注册的邮箱地址是否注册过,使用mongoose查询方法
// 2.验证用户(使用邮箱)是否被注册.如果找得到email说明数据库存在,否则不存在,不存在就实行添加
let user = await User.findOne({
email: req.body.email
})
if (user) { //查询到了,则不能注册,重定向到注册页面
return res.redirect(`/admin/register?message=邮箱已被注册`)
}
(5)密码加密技术bcrypt
// 3.对密码加密
// 3.1生成随机字符串
const salt = await bcrypt.genSalt(10)
// 3.2 加密替换代码
req.body.password = await bcrypt.hash(req.body.password, salt)
(6)将信息添加到数据库
await new User(req.body).save()
//或者
//await User.create(req.body)
(7)重定向到用户页面
res.redirect('/admin/user')
2.登录
(1)创建用户集合,登录用户
- 连接数据库
- 创建用户集合
- 初始化用户
user.js
// 创建用户集合
var mongoose = require('mongoose')
//2.连接并创建数据库
mongoose.connect("mongodb://localhost/blog").then(() => {
console.log('连接成功');
}).catch(() => {
console.log('连接失败');
})
const Schema = mongoose.Schema
var userSchema = new Schema({
username: {
type: String,
required: true, //用户必须
minlength: 2,
maxlength: 20
},
email: {
type: String,
unique: true, //保证唯一性(不重复)
},
password: {
type: String,
required: true
},
role: { //admin超级管理员
type: String,
required: true
},
state: { //0为启用,1为禁用
type: Number,
default: 0
}
})
// 插入数据检测(初始化用户)
// User.create({
// username:'admin',
// email:'333@qq.com',
// password:'admin',
// role:'admin',
// state:0
// }).then(()=>{
// console.log('创建成功');
// }).catch(()=>{
// console.log('创建失败');
// })
//将用户集合作为模块成员导出
// 实例化文档结构(将文档结构发布为模型)
module.exports = mongoose.model('User', userSchema)
(2)视图层设置form提交数据
login.html
<form id="loginForm" action="/login" method="post">
<div class="form-group">
<label>邮件</label>
<input type="email" name="email" class="form-control" placeholder="请输入邮件地址">
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" class="form-control" placeholder="请输入密码">
</div>
<button type="submit" class="btn btn-primary loginForm">登录</button>
</form>
(3)视图层验证。 视图层验证表单内容是否填写,如果没填写完整则阻止登录
视图层js文件
//1.表单序列化
function serializeToJson(form) {
var result={}
var f = form.serializeArray()
f.forEach(function (item) {
result[item.name] = item.value
})
return result
}
//2.判断表单字段
var result = serializeToJson(Form)//序列化获取表单
if (result.email.trim().length == 0) {
alert("请输入邮箱")
// 阻止表单默认提交
return false
}
(4)服务端验证。
- 验证邮箱和密码是否输入(错误状态码400)
- 服务器接收数据,验证表单内容是否填写,如果没填写完整则阻止登录
(4.1)创建登录路由页面
router.get('/login', function (req, res) {
// res.status(200).send("后台管理页面")
// res.send("后台管理页面")
res.render("admin/login.html")
})
(4.2)登录功能操作
router.post('/login', async (req, res) => {
const {
email,
password
} = req.body //解构语法,把对象中的req.body.email的值赋值给了email,把对象中的req.body.password的值赋值给了password
// res.send('ok')
// 1.验证用户是否输入了邮箱和密码(验证form表单传过来的数据是否存在)
if (email.trim().length == 0 || password.trim().length == 0)
return res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
// 2.根据邮箱地址查询用户信息是否存在
// 查询到用户,则user表中有对象,否则为空
let user = await User.findOne({
email
})
if (user) { //查询到了
// 将form传递的密码和数据库中的进行比对
let isValid = await bcrypt.compare(password, user.password)
if (isValid) {
req.session.username=user.username //将用户名存储到session对象
// res.send('登录成功')
req.app.locals.userInfo=user;//将用户信息存放到模板 req.app.locals.名称=变量
res.redirect('/user')
} else {
res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
}
} else { //没查询到
res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
}
})
此处注意:需要另外希望一个err.html文件来显示错误页面
(5)密码比对成功才能登录(密码加密技术bcrypt)
let isValid = await bcrypt.compare(password, user.password)
(6)cookie与session
- 设置session
app.js
const session = require('express-session') //导入express-session
//配置session
app.use(session({
secret: 'secret:key',
saveUninitialized:false,//退出登录不会存储cookie
cookie:{
maxAge:24*60*60*1000 //设置cookie过期时间为一天,如果不设置浏览器关闭则登录状态失效
}
}))
- 使用session
req.session.username = user.username //将用户名存储到session对象
(7)用户退出
点击用户退出按钮删除cookie,重定向到登录页面
//3.用户登录退出
router.get("/admin/logout", (req, res) => {
//删除cookie
req.session.destroy(function () {
//删除Cookie
res.clearCookie('connect.sid') // res.clearCookie(cookie名称)
//重定向到登录
res.redirect('/admin/login')
})
//删除session
//重定向到登录页面
})
(8)登录拦截
- 1.判断是否为登录页面
当页面不是登录的页面,使用拦截器将页面重定向到登录页面
- 2.判断是否为登录状态
判断session是否存在来判断是否为登录状态
//拦截器,拦截登录状态
router.use('/admin', (req, res, next) => {
//1.判断用户是否是登录页面
// 2.判断用户登录状态
//如果登录,将请求放行
//如果未登录,重定向到登录
if (req.url != "/login" && !req.session.username) { //如果未登录,重定向到登录
res.redirect("/admin/login")
} else { //如果登录,将请求放行
next()
}
})
代码
register.js
const express = require('express')
const User = require('../../model/user') //user数据表
const joi = require('joi')
const bcrypt = require('bcrypt')
let router = express.Router() //创建router
//用户首页
router.get('/admin/user', (req, res) => {
res.render("admin/user.html", {
msg: req.session.username
})
})
//用户注册页面
router.get('/admin/register', (req, res) => {
const {
message
} = req.query
res.render("admin/user-edit.html", {
message
})
})
// 用户注册处理
router.post('/admin/register', async (req, res) => {
// 1.验证表单内容是否符合格式
//创建验证规则
const schema = joi.object({
username: joi.string().min(3).max(20).required().error(new Error("用户名设置错误")),
password: joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required().error(new Error("密码设置错误")),
email: joi.string().required().email({
minDomainSegments: 2,
tlds: {
allow: ['com', 'net']
}
}),
role: joi.string().valid('normal', 'admin').required().error(new Error("角色值设置错误")),
state: joi.number().valid(0, 1).required().error(new Error("状态值设置错误")),
})
try { //验证通过执行
// 实现验证
await schema.validateAsync(req.body)
} catch (e) { // 验证错误执行
//重定向到编辑页,并将错误信息传过去
return res.redirect(`/admin/register?message=${e.message}`)
}
// 2.验证用户(使用邮箱)是否被注册.如果找得到email说明数据库存在,否则不存在,不存在就实行添加
let user = await User.findOne({
email: req.body.email
})
if (user) { //查询到了,则不能注册,重定向到注册页面
return res.redirect(`/admin/register?message=邮箱已被注册`)
}
// 3.对密码加密
// 3.1生成随机字符串
const salt = await bcrypt.genSalt(10)
// 3.2 加密替换代码
req.body.password = await bcrypt.hash(req.body.password, salt)
// 3.3
// req.body.password=password
//4.增加信息
await new User(req.body).save()
// await User.create(req.body)
res.redirect('/admin/user')
})
//404页面
router.use('/', (req, res, next) => {
res.send('404')
})
module.exports = router //导出模块
login.js
const express = require('express')
const User = require('../../model/user') //user数据表
const bcrypt = require('bcrypt')
let router = express.Router() //创建router
//路由
//拦截器,拦截登录状态
router.use('/admin', (req, res, next) => {
//1.判断用户是否是登录页面
// 2.判断用户登录状态
//如果登录,将请求放行
//如果未登录,重定向到登录
if (req.url != "/login" && !req.session.username) { //如果未登录,重定向到登录
res.redirect("/admin/login")
} else { //如果登录,将请求放行
next()
}
})
// 1.登录页面
router.get('/admin/login', function (req, res) {
// res.status(200).send("后台管理页面")
// res.send("后台管理页面")
res.render("admin/login.html")
})
// 2.登录功能实现
router.post('/admin/login', async (req, res) => {
const {
email,
password
} = req.body //解构语法,把对象中的req.body.email的值赋值给了email,把对象中的req.body.password的值赋值给了password
// res.send('ok')
// 1.验证用户是否输入了邮箱和密码(验证form表单传过来的数据是否存在)
if (email.trim().length == 0 || password.trim().length == 0)
return res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
// 2.根据邮箱地址查询用户信息是否存在
// 查询到用户,则user表中有对象,否则为空
let user = await User.findOne({
email
})
if (user) { //查询到了
// 将form传递的密码和数据库中的进行比对
let isValid = await bcrypt.compare(password, user.password)
if (isValid) {
req.session.username = user.username //将用户名存储到session对象
// res.send('登录成功')
req.app.locals.userInfo = user; //将用户信息存放到模板 req.app.locals.名称=变量
res.redirect('/admin/user')
} else {
res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
}
} else { //没查询到
res.status(400).render("admin/err.html", {
msg: '邮箱或者密码错误'
}) //状态码400 请求信息有问题
}
})
//3.用户登录退出
router.get("/admin/logout", (req, res) => {
//删除cookie
req.session.destroy(function () {
//删除Cookie
res.clearCookie('connect.sid') // res.clearCookie(cookie名称)
//重定向到登录
res.redirect('/admin/login')
})
//删除session
//重定向到登录页面
})
module.exports = router //导出模块