เนื้อหานี้ เป็นตอนที่ 3 แล้วเกี่ยวกับระบบสมาชิก โดยเราจะมาต่อ
ในส่วนของการบันทึกข้อมูลลงฐานข้อมูล MongoDB ในขั้นตอน
การสมัครสมาชิก การค้นหาและตรวจสอบข้อมูลกับฐานข้อมูล MongoDB
ในขั้นตอนการตรวจสอบอีเมลและการล็อกอินเข้าสู่ระบบสมาชิก
การแสดงข้อมูลใน MongoDB ในขั้นตอนการแสดงข้อมูลหน้าสมาชิก เป็นต้น
สำหรับขั้นตอนในการใช้งาน bcrypt ในการเข้ารหัสข้อมูลก่อนทำการบันทึกลงฐานข้อมูล
ซี่งในที่นี้เราจะทำการเข้ารหัส password ก่อนที่จะบันทึก และมีการตรวจสอบรหัสผ่าน ที่บันทึกไว้
ในหน้าล็อกอิน อย่างไรก็ตามในการใช้งาน bcrypt module ให้เราทำการติดตั้งก่อนใช้งานดังนี้
การติดตั้ง Bcrypt Module
npm install bcrypt --save
การสร้าง Models สำหรับระบบสมาชิก
ในระบบสมาชิก จะมีขั้นตอนการทำงานต่างๆ เช่น การเพิ่มข้อมูล กรณีสมัครสมาชิกใหม่ การค้นหาและตรวจสอบ
ข้อมูลกรณีล็อกอิน การแสดงข้อมูลกรณีล็อกอินสำเร็จและอยู่ที่หน้าสมาชิก เหล่านี้เป็นกระบวนการที่จะเกิดขึ้น ซึ่งเราจะ
สร้างเป็นชุด models ฟังก์ชั่นสำหรับเรียกใช้งาน
แต่ก่อนอื่นสิ่งที่เราต้องมี กรณีใช้งานร่วมกับฐานข้อมูล MongoDB คือ Document Collection ที่เกี่ยวข้อง ในที่เราจะ
สร้างเพิ่มมา 2 collection คือ "lastid" กับ "users"
ทบทวนเกี่ยวกับการใช้งาน MongoDB เบื้องต้นได้ที่ http://niik.in/917
สำหรับ "users" collection เราจะใช้สำหรับเก็บข้อมูลของสมาชิก ส่วน "lastid" เราจะใช้สำหรับเก็บข้อมูล id ล่าสุดของ
ข้อมูลที่เราต้องการประยุกต์ใช้งานในรูปแบบ auto increment โดยใน "lastid" ให้เราเพิ่มข้อมูลเข้าไปเบื้องต้นดังนี้
เราจะกำหนดให้ user_id สำหรับเป็นค่าไว้เก็บ auto increment ของ user โดยจะเริ่มต้นที่ 1000 นั่นคือ ก่อนที่จะมีการ
เพิ่มข้อมูลเข้าไปในระบบ จะมาอ่านค่า user_id ใน "lastid" จากนั้นก็บวกค่าเพิ่มไป 1 แล้วนำไปบันทึกเป็นค่า primary key
ซึ่งเราจะใช้ฟิลด์ "_id" เก็บค่านี้ใน "users" collection
หลังจากเตรียมในส่วนของฐานข้อมูล MongoDB เรียบร้อยแล้ว ให้เราสร้างไฟล์ models ชื่อ users.js ไว้ในโฟลเดอร์
models จะได้รูปแบบไฟล์เบื้องต้นเป็นดังนี้
ไฟล์ users.js เค้าโครง [models/users.js]
const db = require('../config/db') const Users = { email_exists:((req, res) => { return new Promise((resolve, reject)=>{ }) }), login:((req, res) => { return new Promise((resolve, reject)=>{ }) }), register:((req, res) => { return new Promise((resolve, reject)=>{ }) }), userinfo:((req, res) => { return new Promise((resolve, reject)=>{ }) }) } module.exports = Users
ข้างต้นเป็นรูปแบบ Users Model ที่เราจะกำหนด โดยใน Users object จะมี property เป็นฟังก์ชั่นต่างๆ ที่เราจะใช้งาน
ในระบบสมาชิก การทำงานก็ตามชื่อที่กำหนด เช่น email_exists() สำหรับตรวจสอบอีเมล ไม่ให้ข้อมูลอีเมลของสมาชิกซ้ำกัน
เพราะในที่นี้ email ก็คือ username ที่เราจะใช้ล็อกอินเข้าสู่ระบบ นอกนั้นก็มีฟังก์ชั่น login() ฟังก์ชั่น register() และฟังก์ชั่น
userinfo() โดยทั้ง 4 ฟังก์ชั่นจะมี parameter 2 ตัวเหมือนกันที่ส่งเข้ามา คือ req และ res สำหรับการทำงาน เราทราบอยู่
แล้วเบื้องต้นว่า ทั้ง 4 ฟังก์ชั่น จะทำงานร่วมกับฐานข้อมูล ดังนั้นในขั้นตอนการเพิ่ม หรือค้นหาข้อมูล จะมีช่วงที่ต้องรอใน
กระบวนการทำงาน และเราไม่รู้ว่าจะเสร็จ ณ จุดเวลาไหน เราจึงสั่งให้ฟังก์ชั่น return เป็น Promise Object กลับมา
หลักๆ ของการใช้งาน Promise ในฟังก์ชั่นทั้ง 4 จะอยู่ในรูปแบบการทำงานดังนี้
// จะส่งกลับแค่กรณีใด กรณีหนึ่งเท่านั้น คือ resolve หรือ ไม่ก็ reject var promise = new Promise(function(resolve, reject) { if(!error){ resolve(value1) }else{ reject(value2) } })
และในขั้นตอนการใช้งานก็จะอยู่ใรูปแบบ
promise.then( function(result) { /* successful result */ }, function(error) { /* error */ } )
ตัวอย่าง เช่น
promise.then( (value1)=>{ console.log("completion " + value1) }, (value2)=>{ console.log("failure " + value2) } )
จะได้โค้ดแบบเต็มพร้อมคำอธิบายเป็นดังนี้
ไฟล์ users.js [models/users.js]
const db = require('../config/db') const Users = { email_exists:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ // ใช้งานฐานข้อมูล เมื่อพร้อม db.collection('users') .find({ email:req.body.email // ตรวจสอบอีเมล }) .toArray( (error, results) => { if(!error){ // ไม่มี error if(results.length==0){ // ไม่มีอีเมลนี้ในระบบ ใช้สมัครสมาชิกได้ resolve(true) }else{ reject('อีเมลนี้ถูกใช้งานแล้ว') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), login:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ db.collection('users') .find({ // ตรวจสอบข้อมูลสำหรับล็อกอิน ด้วย email และ password ในฐานข้อมูล email:req.body.email, password:req.body.password }) .toArray( (error, results) => { if(!error){ // ถ้าไม่มี error if(results.length > 0){ // มีข้อมูล resolve(results) // ส่งข้อมูลกลับออกไปใช้งาน }else{ reject('อีเมล หรือ รห้สผ่านไม่ถูกต้อง กรุณาลองใหม่') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), register:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ db.collection('lastid') // ดึงข้อมูล user_id ล่าสุดที่บันทึกไว้ใน "lastid" .findOneAndUpdate({id:1},// เงื่อนไขที่ id = 1 { $inc: { user_id: 1 }},(error, results)=>{ // แล้วให้เพิ่มค่า +1 เข้าไปให้กับ user_id if(!error){ // ถ้าไม่มี error let insertID = results.value.user_id+1 // เอาไอดีค่าล่าสุดมาบวกเพิ่ม แล้วไว้ในตัวแปร insertID let user = { // ชุดข้อมูลที่จะบันทึก สำหรับสมัครสมาชิก "_id": insertID, "name": req.body.name, "email": req.body.email, "password": req.body.password } db.collection('users') .insertOne(user, (error, results) => { // เพิ่มข้อมูลสมาชิกใหม่ if(!error){ // ถ้าไม่มี error resolve(results) // ส่งข้อมูลที่บันทึก กลับไปไว้ใช้งาน }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), userinfo:((req, res) => { return new Promise((resolve, reject)=>{ db.then((db)=>{ db.collection('users') .find({ _id:req.session.userID // แสดงข้อมูลผู้ใช้ที่ล็อกอิน และมี _id เท่ากับ session userID }) .toArray( (error, results) => { if(!error){ // ถ้าไม่มี error if(results.length > 0){ // พบข้อมูล resolve(results) // ส่งข้อมูลผู้ที่ล็อกอินกลับไป }else{ reject('ไม่พบข้อมูลผู้ใช้') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }) } module.exports = Users
เมื่อเราได้ชุดคำสั่งทั้งหมดในระบบสมาชิก หรือ Users model เรียบร้อยแล้ว ขั้นตอนต่อไปก็คือการนำไปใช้งาน
ในหน้าต่างๆ เริ่มต้นที่ไฟล์ register.js สำหรับสมัครสมาชิก
ไฟล์ register.js [routes/register.js]
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') const Users = require('../models/users') // ใช้งาน Users model router.route('/') .all((req, res, next) => { // ตัวแปรที่กำหนดด้วย res.locals คือค่าจะส่งไปใช้งานใน template res.locals.pageData = { title:'Register Page' } // ค่าที่จะไปใช้งาน ฟอร์ม ใน template res.locals.user = { name:'', email:'', password:'', confirm_password:'' } // กำหนดหน้าที่ render กรณี error ไม่ผ่านการตรวจสอบข้อมูล req.renderPage = "pages/register" next() }) .get((req, res, next) => { res.render('pages/register') }) .post(validation(schema.register), (req, res, next) => { // เมื่อส่งข้อมูลมาสมัครสมาชิก ตรวจสอบอีเมล Users.email_exists(req, res).then( (validEmail)=>{ // ถ้าอีเมลยังว่าง ยังไม่ถูกใช้งาน // ทำการส่งข้อมูลไปบันทึกลงฐานข้อมูล Users.register(req, res).then( (results)=>{ // บันทึกข้อมูลเรียบร้อย // ส่ง ข้อมูลไปแสดงในหน้า login ให้ผู้ใช้ล็อกอินเพื่อเข้าสู่ระบบ โค้ดด้านล่างตัดบรรทัดใหม่ไม่ให้ยาว res.cookie('flash_message', 'Register Complete!! Please Login to access', {maxAge:3000}) res.redirect('/login') }, (error)=>{ // เกิด error แจ้ง error ในหน้า สมัครสมาชิก ส่งค่าเข้าไปด้วย res.locals.errors res.locals.errors = { "message": error } res.render('pages/register') } ) }, (error)=>{ // เกิด error หรือ อีเมลไม่ว่าง ถูกใช้งานแล่้ว โค้ดด้านล่าง รวมให้อยู่บรรทัดเดียว ทำให้ดูเป็นตัวอย่าง res.locals.errors = { "message": error } res.render('pages/register') } ) }) module.exports = router
ต่อด้วยหน้าล็อกอิน ส่งข้อมูลฟอร์มไปตรวจสอบและสร้างตัวแปร req.session.userID ถ้าล็อกอินสำเร็จ
ไฟล์ login.js [routes/login.js]
const express = require('express') const router = express.Router() const { validation, schema } = require('../validator/users') const Users = require('../models/users') // ใช้งาน Users model router.route('/') .all((req, res, next) => { // ตัวแปรที่กำหนดด้วย res.locals คือค่าจะส่งไปใช้งานใน template res.locals.pageData = { title:'Login Page' } // ค่าที่จะไปใช้งาน ฟอร์ม ใน template res.locals.user = { email:req.session.email || '', password:req.session.password || '', remember:req.session.remember || '' } // กำหนดหน้าที่ render กรณี error ไม่ผ่านการตรวจสอบข้อมูล req.renderPage = "pages/login" next() }) .get((req, res, next) => { // ตรวจสอบว่ามี ค่า cookie ค่านี้หรือไม่ if(req.cookies.flash_message){ // ถ้ามี นำค่าไปใกำหนดในตัวแปร res.locals เพื่อใช้งานใน template res.locals.success = { message:req.cookies.flash_message } } res.render('pages/login') }) .post(validation(schema.login), (req, res, next) => { // สมมติเรากำหนดเป็น 15 วัน let MAX_AGE = 1000 * 60 * 60 * 24 * 15 if(req.body.remember){ req.session.email = req.body.email req.session.password = req.body.password req.session.remember = req.body.remember req.session.cookie.maxAge = MAX_AGE }else{ delete req.session.email delete req.session.password delete req.session.remember } // ส่งข้อมูลไปตรวจสอบในฐานข้อมูล สำหรับทำการล็อกอินเข้าใช้งาน Users.login(req, res).then( (results)=>{ // ล็อกอินสำเร็จ พบข้อมูลผู้ใช้ตาม email และ password ที่แจ้ง console.log(results) req.session.userID = results[0]._id // กำหนดตัวแปร session userID req.session.isLogined = true // กำหนดตัวแปร session สถานะการล็อกอิน res.redirect('/me') // ไปยังหน้าสมาชิก }, (error)=>{ // ล็อกอินไม่ผ่าน แจ้ง error หน้า ล็อกอิน console.log(error) res.locals.errors = { "message": error } res.render('pages/login') } ) }) module.exports = router
และส่วนสุดท้ายหน้าสมาชิก ทำการดึงข้อมูลจากฐานข้อมูล โดยใช้ session userID ไปตรวจสอบ
และแสดงข้อมูล
ไฟล์ dashboard.js [routes/dashboard.js]
const express = require('express') const router = express.Router() const Users = require('../models/users') // ใช้งาน Users model router.route('/') .get((req, res, next) => { res.locals.pageData = { title:'Dashboard Page' } // ดึงข้อมูลสมาชิก จาก session userID Users.userinfo(req, res).then( (user)=>{ // มีข้อมูล // ส่งข้อมูลไว้ในตัวแปร สำหรับใช้งานใน template res.locals.user = user[0] res.render('pages/dashboard') } ) }) router.route('/logout') .get((req, res, next) => { // กรณี logout ลบ session ค่าที่เกี่ยวข้องกับระบบสมาชิก แล้วลิ้งค์ไปหน้าล็อกอิน delete req.session.isLogined delete req.session.userID res.cookie('flash_message', 'Logout Complete!!',{maxAge:3000}) res.redirect('/login') }) module.exports = router
ในหน้า สมาชิก เรามีการส่งข้อมูล ที่ได้มาจากการดึงจากฐานข้อมูลมาไว้ในตัวแปร res.locals.user เพื่อนำไป
แสดงในไฟล์ template ดังนั้น ให้เราแก้ไขไฟล์ template สำหรับแสดงข้อมูลสมาชิก เป็นดังนี้
ไฟล์ dashboard.ejs [views/pages/dashboard.ejs]
<!doctype html> <html> <?- include('../partials/head') -?> <body> <?- include('../partials/header') -?> <?- include('../partials/nav') -?> <div class="container"> <h1 class="text-center">Hello</h1> <div class="text-center my-3 border border-success"> <?= user.name ?> <br/> <?= user.email ?> </div> <div class="text-center"> </div> </div> <?- include('../partials/footer') -?> </body> </html>
เราใช้รูปแบบอย่างง่าย แสดงแค่ชื่อ กับอีเมล
ต่อไปเราทดสอบการทำงาน โดยให้ทำการสมัครสมาชิกเข้าไป จะได้ผลลัพธ์เป็นดังนี้
กรอกข้อมูลหน้าสมัครสมาชิก
เสร็จแล้ว ทำการล็อกอินเข้าระบบ
หน้าสมาชิก แสดงข้อมูลผู้ใช้
ไปดูข้อมูลในฐานข้อมูล เมื่อเราทำการสมัครสมาชิกเข้ามา
จะเห็นว่าค่า user_id ที่อยู่ใน "lastid" จะเป็นค่า user_id ล่าสุดที่เราเพิ่มลงไปในฐานข้อมูล
เมื่อดูใน "users" จะเห็นว่า เราได้ทำการเพิ่ม user ใหม่เข้ามา ค่า _id เป็น primary key ที่เราใช้เก็บ user_id
มีค่าบวกเพิ่มจาก 1000 เป็น 1001 และเราเอาค่า 1001 อัพเดทกลับไปยัง "lastid"
ในข้อมูลข้างต้น เราจะเห็นว่า password ที่บันทึกไว้ ไม่ได้มีการเข้ารหัสเอาไว้ ซึ่งไม่มีความปลอดภัย ห้วข้อหน้า
เรามาดู การเข้ารหัส password ด้วย bcrypt
การใช้งาน Bcrypt เข้ารหัส Password
หลังจากเราได้ติดตั้ง bcrypt module สำหรับเข้ารหัสข้อมูลไปแล้ว ในตอนเริ่มต้นของบทความ ต่อไปเราจะมาดูวิธี
การใช้งานเบื้องต้นกัน โดยเราจะเลือกใช้ในรูปแบบ Promise ดังนี้
การเข้ารหัสข้อมูล
bcrypt.hash(myPlaintextPassword, saltRounds).then(function(hash) { // นำค่า hash ไปใช้งาน เช่น บันทึกค่า hash ที่ได้ลงฐานข้อมูล })
การตรวจสอบข้อมูลที่เข้ารหัส
// ดึงค่า hash จากฐานข้อมูล แล้วนำค่ามาตรวนสอบ กับข้อมูลที่ต้องการ bcrypt.compare(myPlaintextPassword, hash).then(function(result) { // result == true ถ้าข้อมูลถูกต้อง และ เป็น false ถ้าไม่ถูกต้อง })
การประยุกต์ใช้ Bcrypt กับ ระบบสมาชิก
ในการใช้งานในระบบสมาชิกของเรา จะมีอยู่ 2 ฟังก์ชั่น ที่เกี่ยวข้องกับการใช้งาน bcrypt คือฟังก์ชั่น register()
และฟังก์ชั่น login() ซึ่งอยู่ในไฟล์ users.js [models/users.js] ซึ่งเป็นไฟล์ Users Models ให้แก้ไขไฟล์เป็นดังนี้
ไฟล์ users.js [models/users.js]
const db = require('../config/db') const bcrypt = require('bcrypt') // ใช้งาน bcrypt module const saltRounds = 10 // กำหนดค่า salt const Users = { email_exists:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ db.collection('users') .find({ email:req.body.email }) .toArray( (error, results) => { if(!error){ if(results.length==0){ resolve(true) }else{ reject('อีเมลนี้ถูกใช้งานแล้ว') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), login:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ db.collection('users') .find({ email:req.body.email }) .toArray( (error, results) => { if(!error){ if(results.length > 0){ let hash = results[0].password let password = req.body.password bcrypt.compare(password, hash).then((result)=>{ if(result == true) resolve(results) if(result == false) reject('รห้สผ่านไม่ถูกต้อง กรุณาลองใหม่') }) }else{ reject('อีเมล หรือ รห้สผ่านไม่ถูกต้อง กรุณาลองใหม่') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), register:((req, res) => { return new Promise((resolve, reject)=>{ res.locals.user = req.body db.then((db)=>{ db.collection('lastid') .findOneAndUpdate({id:1}, { $inc: { user_id: 1 }},(error, results)=>{ if(!error){ let password = req.body.password bcrypt.hash(password, saltRounds).then((hash)=>{ let insertID = results.value.user_id+1 let user = { "_id": insertID, "name": req.body.name, "email": req.body.email, "password": hash } db.collection('users') .insertOne(user, (error, results) => { if(!error){ resolve(results) }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }), userinfo:((req, res) => { return new Promise((resolve, reject)=>{ db.then((db)=>{ db.collection('users') .find({ _id:req.session.userID }) .toArray( (error, results) => { if(!error){ if(results.length > 0){ resolve(results) }else{ reject('ไม่พบข้อมูลผู้ใช้') } }else{ reject('เกิดข้อผิดพลาด กรุณาลองใหม่') } }) }) }) }) } module.exports = Users
เสร็จแล้วให้เรา logout ออกจากสมาชิกทดสอบก่อนหน้า แล้วทำการลบข้อมูล เดิมในฐานข้อมูลออก ลบเฉพาะ
ข้อมูลใน "users" แล้วทำการเพิ่มข้อมูล โดยสมัครสมาชิกใหม่อีกครั้ง ซึ่งเดิม รหัสผ่านเราคือ 111111 เราจะใช้ค่า
เดิมทั้งหมด เมื่อสมัครสมาชิกใหม่เรียบร้อยด้วยข้อมูล จะได้ผลลัพธ์เป็นดังนี้
จะเห็นว่าในส่วนของ password ที่เราพิมพ์เป็น 111111 จะถูกเข้ารหัสเรียบร้อย และส่วนของ _id ก็รันค่าเพิ่มอัตโนมัติ
และเมื่อเราทดสอบการล็อกอิน ก็สามารถล็อกอิน โดยกรอกรหัสผ่านเป็น 111111 ล็อกอินเข้าสู่ระบบได้ปกติ
เรามาลองทดสอบเงื่อนไข กรอกผิดทั้ง email และ password กับกรอกผิดเฉพาะ password ดูว่าจะได้ผลลัพธ์
เป็นอย่างไร ตามลำดับ
ตอนนี้ระบบสมาชิกของเรา มีขั้นตอนการทำงานถูกต้อง และเรียบร้อยไปพอสมควรแล้ว
แต่ยังมีเงื่อนไขว่า เราจะส่งค่า session ไปหน้าอื่นๆ ยังไง เช่น ในไฟล์ nav.ejs ที่เป็น บางส่วนของ layout
ที่มีการเรียกใช้ทุกๆ หน้า ถ้าเราต้องการใช้ตัวแปร session อย่าง isLogined มากำหนดการแสดงของเมนู ว่าถ้ายังไม่ได้ล็อกอิน
ให้แสดงเฉพาะเมนู login และ register และถ้าล็อกอินแล้ว ให้แสดงเฉพาะเมนู logout ซึ่งเราทราบดีว่า ถ้าจะส่งค่าไปใช้งาน
ใน template เราก็แค่กำหนด res.locals โดยเพิ่ม property ค่าที่ต้องการ เช่น
if(typeof req.session !== 'undefined'){ res.locals.session = req.session }
นั่นหมายความว่า เราต้องไปไล่กำหนดในทุกๆ หน้า เช่น หน้า index.js , login.js ..... dashboard.js ซึ่งคงไม่สะดวกแน่
ดังนั้น เราจะสร้างฟังก์ชั่น middleware มาจัดการ ดังนี้
ให้เราสร้างไฟล์ session.js ไว้ในโฟลเดอร์ config แล้วกำหนดโค้ดเป็นดังนี้
ไฟล์ session.js [config/session.js]
module.exports = { useSession:(req, rew, next)=>{ // ถ้ามีตัวแปร sewsion if(typeof req.session !== 'undefined'){ // ให้กำหนดต่าให้กับ app.locals ใน property ชื่อ session // ให้มีค่าเท่ากับ session object req.app.locals.session = req.session } next() } }
การใช้คำสั่ง req.app.locals คือการกำหนดค่าให้กับ app.locals อย่างที่เราทราบว่า res.locals จะเป็นการกำหนด
scope ของตัวแปร ให้สามารถใช้งานได้ในหน้า view หรือ template ที่ render แต่การใช้งาน app.locals เป็นการกำหนด
scope ของตัวแปร ให้สามารถใช้งานได้ทั้ง web หรือทั้ง app ทุก view และ template การกำหนด
req.app.locals.session = req.session หรือก็คือ app.locals.session = req.session จะทำให้เราสามารถเรียกใช้งาน
ตัวแปร session ใน template ต่างๆ ได้
สมมติว่า ตอนนี้ตัวแปร session ใน req.session มีแค่ req.session.isLogined กับ req.session.userID
การกำหนดให้ req.app.locals.session = req.session ก็จะทำให้เราสามารถใช้งานตัวแปร app.locals.session
ที่ชื่อ session.isLogined กับ session.userID ในไฟล์ template ได้ แบบนี้เป็นต้น
หากกลัวสับสน ก็อาจจะใช้เป็น req.app.locals._session แทนก็ได้ เวลาเรียกใช้ใน template ก็จะเป็น
_session.isLogined กับ _session.userID
หลังจากสร้างไฟล์ sesson.js เรียบร้อยแล้ว ให้เราเรียกใช้งานในไฟล์ app.js ดังนี้
ไฟล์ app.js
const express = require('express') // ใช้งาน module express const app = express() // สร้างตัวแปร app เป็น instance ของ express const path = require('path') // เรียกใช้งาน path module const cookieParser = require('cookie-parser') const session = require('express-session') const store = require('./config/storeDb') const { authorize } = require('./config/auth') const { useSession } = require('./config/session') const createError = require('http-errors') // เรียกใช้งาน http-errors module const port = 3000 // port // ส่วนของการใช้งาน router module ต่างๆ const indexRouter = require('./routes/index') const loginRouter = require('./routes/login') const registerRouter = require('./routes/register') const dashboardRouter = require('./routes/dashboard') // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.set('view options', {delimiter: '?'}); // app.set('env','production') app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use(cookieParser()) app.use(express.static(path.join(__dirname, 'public'))) //app.set('trust proxy', 1) // trust first proxy app.use(session({ name:'sid', // ถ้าไม่กำหนด ค่าเริ่มต้นเป็น 'connect.sid' secret: 'my ses secret', store:store, resave: true, saveUninitialized: true })) app.use(useSession) // เรียกใช้งาน indexRouters app.use('/', indexRouter) app.use('/login', authorize('/me', false), loginRouter) app.use('/register', authorize('/me', false), registerRouter) app.use('/me', authorize('/login', true), dashboardRouter) // ทำงานทุก request ที่เข้ามา app.use(function(req, res, next) { var err = createError(404) next(err) }) // ส่วนจัดการ error app.use(function (err, req, res, next) { // กำหนด response local variables res.locals.pageData = { title:'Error Page' } res.locals.message = err.message res.locals.error = req.app.get('env') === 'development' ? err : {} // กำหนด status และ render หน้า error page res.status(err.status || 500) // ถ้ามี status หรือถ้าไม่มีใช้เป็น 500 res.render('pages/error') }) app.listen(port, function() { console.log(`Example app listening on port ${port}!`) })
เสร็จแล้ว เรามาลองเรียกใช้ และกำหนดค่าในไฟล์ nav.ejs เป็นดังนี้
ไฟล์ nav.ejs [views/partials/nav.ejs]
<nav class="text-center"> THIS IS NAV <br> <? if(!session.isLogined || session.isLogined == false){ ?> <a href="/login">Login</a> | <a href="/register">Register</a> <? }else{ ?> <a href="/me/logout">Logout</a> <? } ?> </nav>
ทดสอบใช้งาน ไปหน้าแรกของ Homepage ที่ path:"/" เมื่อเราได้ทำการล็อกอินอยู่
และเมื่อเรา logout
จะเห็นว่าส่วนของเมนู ใน nav.ejs มีการใช้งานค่าตัวแปร ที่เราส่งมา กำหนดเงื่อนไขในการแสดงของเมนู
เนื้อหาตอนหน้า เรายังอยู่ในเนื้อหาเกี่ยวกับระบบสมาชิก จะมาดูในเรื่องของการป้องการ Cross-Site Request Forgery
หรือ XRSF / CSRF attack เช่น การส่งข้อมูลจากที่อื่น มาทำการล็อกอินระบบเว็บของเรา / หรือการอาศัยการคงอยู่ของ
session ในระบบของเราเข้ามาโจมตีเว็บไซต์ คร่าวๆ ประมาณนี้เป็นต้น รอติดตาม