18.注冊和登錄


准備

技術棧

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 //導出模塊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM