NODE 基於express 框架和mongoDB的cookie和session認證 和圖片的上傳和刪除

語言: CN / TW / HK

原始碼地址

https://gitee.com/zyqwasd/mongdbSession

本專案的mongodb是本地的mongodb 開啟方法可以百度一下 埠是預設的27017

頁面效果

1. 註冊登入頁

2. 上傳的圖片 和刪除資料庫的資料和伺服器上的圖片

前端程式碼 因為是個小專案就用原生寫了

1. 這個是login 頁面一個註冊模組一個登入模組

 <!-- 引入axios 方便操作 -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    //--------------------- 頁面的切換
    const registerPage = document.querySelector(".register")
    const loginPage = document.querySelector(".login")
    register.onclick = () => {
        loginPage.style.display = "none"
        registerPage.style.display = "block"
    }
    login.onclick = () => {
        loginPage.style.display = "block"
        registerPage.style.display = "none"
    }
    //-----------------------註冊頁
    reg_submit.onclick = () => {
        if (!(reg_username.value) && !reg_password.value)
            return alert("不能為空");
        const fd = new FormData()// 建立一個表單 把資料放進去 發給後端
        fd.append("avatar", file.files[0]);
        fd.append("password", reg_password.value)
        fd.append("username", reg_username.value)
        // 傳送頭像和使用者名稱密碼去註冊
        axios.post("/login", fd).then(res => {
            alert("註冊" + (res.data.ok ? "成功" : "失敗"))
            location.reload();
        })
    }
    // ----------------------登入頁
    submit.onclick = () => {
        if (!(username.value) && !password.value)
            return alert("不能為空");
        axios.post("/login/find", {
            username: username.value,
            password: password.value
        })
            .then(res => {
                console.log(res.data)
                if (res.data.ok) { //設定username 和 頭像便於首頁顯示
                    localStorage.setItem("username", username.value)
                    localStorage.setItem("avatar", res.data.avatar)
                    location.href = '/'
                } else {
                    alert("密碼錯誤")
                }
            })
    }
</script>

2. 首頁程式碼

 fetch("/api")
    .then(res => res.json())
    .then(res => {
      render(res)
    })
  username.innerHTML = localStorage.getItem("username")
  img.src = localStorage.getItem("avatar")
  function render(arr) {//渲染頁面 
    table.innerHTML = arr.map((item, index) => ` 
    <tr id='${item._id}' data-src='${item.avatar}'>
      <td>${index}</td>
      <td>${item.username}</td>
      <td><img src="${item.avatar}" alt=""></td>
      <td><button id='del'>del</button></td>
    </tr>
    `).join("")
  }
  table.onclick = (e) => {
    if (e.target.nodeName === "BUTTON") {
      const parent = e.target.parentNode.parentNode
      const src = parent.dataset.src
      // 發請求刪除對應資料庫的內容
      fetch(`/api?id=${parent.id}&src=${src} `, { method: 'delete' })
        .then(res => res.json()).then(res => {
          // 刪除對應的tr 
          res.ok && parent.remove()
        })
    }
  }

後端程式碼

1. 因為時間問題就用了express生成器 可以去expresse 官網檢視 獲取下載我的程式碼

連線mongodb

1. 建立一個配置資料夾config 下載安裝mongoose 模組 連線資料庫 (一定要先開啟資料庫)

const mongoose = require("mongoose");
// 因為是演示就自己在本地搭了一個數據庫
// 連線好了資料庫並且 建立了一個叫users 的資料庫
mongoose.connect("mongodb://127.0.0.1:27017/users")

2. 在www 檔案下引入

// 引入資料庫
require("../config/mongoose")

3. 因為mongodb的資料自由度高所有配置一個模板來規定資料型別  在model資料夾/userModel

const mongoose = require("mongoose")
// 定義資料型別
const modelType = {
    username: String,
    password: String,
    avatar: String,
}

const userModel = mongoose.model("user", new mongoose.Schema(modelType));

module.exports = userModel

配置multer 模組在app.js中的程式碼

1.在app 中引入multer模組來處理資料和配置檔案下載的路徑

// 引入multer
const multer = require("multer")
// 部署放頭像的檔案
const upload = multer({ dest: "public/uploads/" })

2. 在app 中處理login 上傳來的圖片把他放入到指定資料夾中  如果想知道如何上傳多個檔案可以私聊我

// 處理檔案上傳資料 single 是上傳單個檔案 array是上傳多個檔案
app.use("/login", upload.single("avatar"), loginRouter);

配置session 在app.js中程式碼

1. 在app.js中引入express-session 和 connect-mongo

// 引入session 模組
const session = require("express-session");
// 引入  connect-mongo 這個是儲存cookie 到mongodb 中自動刪除和更新
const MongoStore = require("connect-mongo")

2.配置初始化配置session

// 配置session 
app.use(session({
  name: "mySession",
  secret: "session secret",//配置祕鑰
  resave: true,//重新設定cookie 值可以重新計時
  saveUninitialized: true,//初始化session  一開始就給伺服器傳送一個cookie 
  cookie: {
    maxAge: 1000 * 60 * 60, //設定過期時間
    secure: false,//是否在安全模式下就是https 下訪問
  },
  rolling: true,//表示在超時前重新整理就會從新計時cookie 為false 表示超時前重新整理多少次都會按照第一次開始計時
  // 配置cookie 存入到MongoDB 中
  store: MongoStore.create({
    mongoUrl: 'mongodb://127.0.0.1:27017/my_session',//連結資料庫
    ttl: 1000 * 60 * 60, //設定過期時間
  })
}))

3. 全域性攔截認證session

// 全域性攔截session 
app.use((req, res, next) => {
  if (req.url.includes("login")) return next()
  // 登入成功了就給session掛載一個user屬性
  if (req.session.user) {
    // 如果有就放行重新設定session 值
    req.session.myDate = Date.now();
    next()
  } else {
    // 因為我是又有後端渲染又有前端的請求所以我們要判斷
    req.url.includes("api") ?
      // 如果是api 就send  ok:1
      res.status(401).send({ ok: 1 })
      // 如果是 頁面的請求就跳到login 頁面
      : res.redirect("/login")
  }
})

處理login頁面資料

1. 在router中的路徑處理

const express = require("express");
const controller = require('../controller/userController');
const router = express.Router()

router.get("/", (req, res) => {
    res.render("login")
})

// 註冊來的資料
router.post("/", controller.create)
// 登入來的資料
router.post("/find", controller.find)

module.exports = router

處理資料響應每一個介面的處理函式 因為程式碼少就沒有多分

const userService = require("../service/userService")
const controller = {
    // 建立資料到mongodb中註冊頁傳來的
    async create(req, res) {
        // 資料解構出來
        const { username, password } = req.body
        // 如果上傳頭像就儲存上傳來的 
        // 如果沒有上傳來就走預設頭像
        const avatar = req.file ?
            "/uploads/" + req.file.filename :
            "/images/avatar.jpeg";
        // 去service模組建立資料到資料庫中
        await userService.create(username, password, avatar);
        // 返回前端資料
        res.send({ ok: 1 })
    },
    async find(req, res) {
        const { username, password } = req.body;
        // 用async  和await 來去資料庫查詢資料 主要程式碼在service中
        let data = await userService.find(username, password)
        if (data.length) {
            // 如果驗證通過了就設定一個session值
            req.session.user = data[0]
            // 如果找到了 返回前端一個當前登入使用者的頭像
            res.send({ ok: 1, avatar: data[0].avatar })
        } else {
            res.send({ ok: 0 })
        }
    },
    // 查詢所有資料用來渲染主頁 顯示頁面 因為都是非同步的所以用了async 和await 
    async findAll(req, res) {
        let data = await userService.findAll()
        res.send(data)
    },
    // 前端發來的刪除使用者請求
    async delete(req, res) {
        // 把id 和圖片的src 結構出來
        const { id, src } = req.query
        // 去刪除資料庫中的資料
        let data = await userService.delete(id)
        // 去刪除本地檔案 因為程式碼少懶得分一個模組了
        await userService.deleteFile(src)
        // 給前端傳一個布林值這個如果刪除了就是true 沒有就是false
        res.send({ ok: data.acknowledged })
    }
}

module.exports = controller

處理資料庫和資料夾中的資料

const userModel = require("../model/userModel")
// 引入fs 模組 因為是非同步所以要用promises
const fs = require("fs").promises
const service = {
    // 把資料儲存到資料庫中
    create(username, password, avatar) {
        // 用我們之前的模板直接來建立資料
        return userModel.create({
            username, password, avatar,
        })
    },
    find(username, password) {
        // 查詢資料給前面模組返回回去 條件是傳來的值
        return userModel.find({ username, password })
    },
    findAll() {
        // 只獲取到["username", "avatar"]兩個欄位
        return userModel.find({}, ["username", "avatar"])
        // 當然因為資料量少沒有搞分頁什麼的
        // find().count()是獲取總的條數
        // sort() 按照什麼排序
        // skip ()跳過幾條
        // limit() 要幾條 
        // 也蠻簡單的可以試試
    },
    delete(id) {
        // 刪除資料庫中的資料
        return userModel.deleteOne({ _id: id })
    },
    async deleteFile(src) {
        // 如果是預設資料 我把資料儲存到public /images 中了其他的在uploads 中
        if (src.includes("/images")) return;
        // 因為刪除檔案沒有檔案會報錯影響伺服器 就try catch 處理一下
        try {
            return data = await fs.unlink(`./public/${src}`)

        } catch (error) {
            return false
        }

    }
}

module.exports = service