統整:實作會員註冊系統
前置作業
安裝 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