[BE201] Express & Sequelize part 4


Posted by yymarlerr on 2021-08-24

統整:實作會員註冊系統

前置作業

安裝 bcrypt 幫密碼作 hash

npm install bcrypt

實作

index.js

const express = require('express')
const bodyParser = require('body-parser')
const session = require('express-session')
const flash = require('connect-flash')
const db = require('./db')
const app = express()
const port = 5001

const todoController = require('./controllers/todo'
)
const userController = require('./controllers/user')

app.set('view engine', 'ejs')

app.use(bodyParser.urlencoded({ extended: false })) 
app.use(bodyParser.json()) 
app.use(flash())

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
}))

app.use((req, res, next) => {
  res.locals.username = req.session.username
  res.locals.errorMessage = req.flash('errorMessage')
  next()
})

function redirectBack(req, res) { // 自己寫的 Middleware
  res.redirect('back')
}

app.post('/todos', todoController.newTodo)
app.get('/todos', todoController.getAll) 
app.get('/todos/:id', todoController.get)
app.get('/', (req, res) => {
  res.render('user/index')  // 首頁
})

app.get('/login', (req, res) => {
  res.render('login')
})

app.get('/login', userController.login)
app.post('/login', userController.handleLogin, redirectBack)
app.get('/logout', userController.logout)
app.get('/register', userController.register)
app.post('/register', userController.handleRegister, redirectBack)

app.post('/login', (req, res) => {
  if (req.body.password === "abc") {
    req.session.isLogin = true 
    res.redirect('/') 
  } else {
    req.flash('errorMessage', 'Please input the correct password')
    res.redirect('/login')
  }
})

app.get('/logout', (req, res) => {
  req.session.isLogin = false
  res.redirect('/')
})

app.listen(port, () => {
  db.connect()
  console.log(`hello world, listening on port ${port}`)
})

controller

  • user.js
const userModel = require('../models/user')
const bcrypt = require('bcrypt')
const saltRounds = 10; // 可以調整密碼的複雜程度

const userController = {
  get: (req, res) => {

  },

  login: (req, res) => {
    res.render('login')
  },

  handleLogin: (req, res, next) => {
    const {username, password} = req.body
    if (!username || !password) {
      req.flash('errorMessage', '資料填寫不齊全')
      return next()
    }

    userModel.get(username, (err, user) => {
      if (err) {
        req.flash('errorMessage', err.toString())
        return next() // 如果不導回其他頁面,瀏覽器會不知道怎麼處理(會一直轉圈圈)
      }
      if (!user) {
        req.flash('errorMessage', '使用者不存在') // 帳號密碼錯誤代表找不到 user
        return next()
      }
      bcrypt.compare(password, user.password, (err, isSuccessful) => {
        if (err || !isSuccessful) {
          req.flash('errorMessage', err.toString())
          return next()
        }
        req.session.username = username
        res.redirect('/')
    });
    })

  },

  logout: (req, res) => {
    req.session.username = null
    res.redirect('/')
  },

  register: (req, res) => {
    res.render('user/register')
  },

  handleRegister: (req, res, next) => {
    // ES6 解構語法
    const {username, nickname, password} = req.body
    if (!username || !nickname || !password) {
      req.flash('errorMessage', '缺少必要欄位') // 這邊用 return 的話,下面的程式碼就不會執行到,用 if else 會有很多層結構
      return next()
    }

    bcrypt.hash(password, saltRounds, (err, hash) => { // 為非同步
      if (err) {
        req.flash('errorMessage', err.toString())
        return next()
      }

      userModel.add({ // hash 過後再將密碼寫進去
        username,
        nickname,
        password: hash
      }, (err) => {
        if (err) {
          req.flash('errorMessage', err.toString())
          return next()
        }
        req.session.username = username // username 這個 key 如果有值代表登入成功了
        res.redirect('/')
      })
    })
  }

}

module.exports = userController

view

  • register.ejs
<h1>註冊頁面</h1>

<h2><%= errorMessage %></h2>
<form method="POST" action="/register">
  <div>Username: <input type="text" name="username" /></div>
  <div>Nickname: <input type="text" name="nickname" /></div>  
  <div>Password: <input type="password" name="password" /></div>
  <input type="submit" />
</form>
  • login.ejs
<h1>登入頁面</h1>

<h2><%= errorMessage %></h2>
<form method="POST" action="/login">
  <div>Username: <input type="text" name="username" /></div>
  <div>Password: <input type="password" name="password" /></div>
  <input type="submit" />
</form>
  • index.js (首頁)
<h1>簡易會員系統</h1>

<!--先判斷是否有登入-->
<% if (username) { %>
  <a href='/logout'>登出</a>
<% } else { %>
  <a href='/register'>註冊</a>
  <a href='/login'>登入</a>
<% } %>
  • Template Engine 已經做好 escape,如果 <%- %> 用減號代表不做 escape

models

  • user.js
const db = require('../db')

const userModel = {
  add: (user, cb) => { // 傳進 user 這個 Object
    db.query(
      'INSERT INTO yide_users (username, password, nickname) VALUES (?, ?, ?)', 
      [user.username, user.password, user.nickname],
      (error, results) => { 
        if (error) return cb(error);
        cb(null)
    });
  },

  get: (username, cb) => {
    db.query(
      'SELECT * FROM yide_users WHERE username = ?', [username], 
      (error, results) => { 
        if (error) return cb(error);
        cb(null, results[0])
    });
  }
}

module.exports = userModel









Related Posts

CS50 Internet Primer

CS50 Internet Primer

Single Page Application 單頁應用

Single Page Application 單頁應用

DNS, Lock, NoSQL vs SQL and ACID

DNS, Lock, NoSQL vs SQL and ACID


Comments