准備
技術棧
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 //導出模塊