DEV Community

Akkarapon Phikulsri
Akkarapon Phikulsri

Posted on

NodeJS 101 —  Part 4 🔐 Authorization with JWT

เนื้อหาดังต่อไปนี้เป็นเนื้อหาเมื่อปี 2023 ถูกปรับปรุงเรียบเรียงใหม่ 2026 ขอให้สนุกกับการเริ่มต้นกับโลก Programming อย่างภาษาง่าย ๆ ด้วย JavaScript กันเน่อ :)

📖 คำนำ

เอกสารฉบับนี้เป็นการพัฒนา web service หรือ RESTful API โดยใช้ภาษา JavaScript Node.js ร่วมกับเฟรมเวิร์กและไลบรารี่ต่าง ๆ ได้แก่:

  • 🌐 Express HTTP Framework - สำหรับสร้าง web server
  • 🗄️ Sequelize - ORM สำหรับจัดการฐานข้อมูล
  • 💾 MySQL - ฐานข้อมูลเชิงสัมพันธ์
  • 🔧 POSTMAN - สำหรับทดสอบ API

Section 4-1 - 🔑 JWT Authentication

📚 JWT คืออะไร?

JWT (JSON Web Token) เป็นมาตรฐานสำหรับสร้าง access token ที่ใช้ในการยืนยันตัวตน (Authentication) และกำหนดสิทธิ์ (Authorization)

🔍 องค์ประกอบของ JWT:
  1. Header - ระบุประเภท token และ algorithm
  2. Payload - ข้อมูลที่ต้องการส่ง (เช่น user ID)
  3. Signature - ใช้สำหรับตรวจสอบความถูกต้องของ token

📦 ติดตั้ง jsonwebtoken

npm install jsonwebtoken
Enter fullscreen mode Exit fullscreen mode

📥 Import jsonwebtoken

const jwt = require('jsonwebtoken');
Enter fullscreen mode Exit fullscreen mode

🔐 สร้าง Login Controller

เพิ่มในไฟล์: controllers/student.controller.js

const jwt = require('jsonwebtoken');

exports.login = async (req, res) => {
    try {
        const id = req.body.stdId;
        const pass = req.body.stdPass;

        // ค้นหานักศึกษาจากฐานข้อมูล
        const response = await studentModel.findOne({
            where: {
                std_id: id,
                std_pass: pass
            },
            raw: true
        })

        if (response) {
            // ⚠️ ในทางปฏิบัติ ควรเก็บ SECRETKEY ไว้ใน .env
            const SECRETKEY = process.env.JWT_SECRET || "secret1234";

            // สร้าง JWT token
            const token = jwt.sign({
                std_id: response.stdId,
            }, SECRETKEY, {
                expiresIn: "1h", // Token หมดอายุใน 1 ชั่วโมง
            });

            // ลบ password ออกจาก response
            delete response.stdPass;

            res.status(200).json({
                message: "login was successfully",
                payload: { ...response, token }
            })
        } else {
            res.status(401).json({
                message: "Invalid credentials"
            })
        }

    } catch (error) {
        res.status(500).json({
            message: error.message || "login student was failed"
        })
    }
}
Enter fullscreen mode Exit fullscreen mode
🔍 อธิบายโค้ด:
  • jwt.sign() - สร้าง JWT token
  • expiresIn: "1h" - กำหนดอายุ token (1 ชั่วโมง)
  • delete response.stdPass - ลบ password ก่อนส่ง response
  • status(401) - HTTP status code สำหรับ Unauthorized

⚠️ คำเตือน: ในทางปฏิบัติ ควรเก็บ SECRETKEY ไว้ในไฟล์ .env หรือ environment variables เพื่อความปลอดภัยนะครับ


🛣️ เพิ่ม Login Route

เพิ่มในไฟล์: routes/student.route.js

router.post("/login", studentController.login);
Enter fullscreen mode Exit fullscreen mode
🧪 ทดสอบ Login API
POST http://localhost:5000/api/student/login
Content-Type: application/json

{
  "stdId": "12345",
  "stdPass": "password123"
}
Enter fullscreen mode Exit fullscreen mode
✅ Response ที่คาดหวัง:
{
  "message": "login was successfully",
  "payload": {
    "stdId": "12345",
    "stdName": "John Doe",
    "facId": 1,
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
Enter fullscreen mode Exit fullscreen mode

Section 4-2 - 🛡️ สร้าง Middleware สำหรับ Verify Token

📁 สร้างโฟลเดอร์ Middlewares

mkdir middlewares
Enter fullscreen mode Exit fullscreen mode

🔒 สร้าง Verify Token Middleware

ไฟล์: middlewares/verify-token.js

const jwt = require('jsonwebtoken');

module.exports = {
    verifyToken(req, res, next) {
        let token;

        // ตรวจสอบ token จากหลายแหล่ง
        if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
            // Token จาก Authorization header: Bearer <token>
            token = req.headers.authorization.split(' ')[1];
        } else if (req.query && req.query.token) {
            // Token จาก query parameter: ?token=<token>
            token = req.query.token;
        } else if (req.cookies && req.cookies.token) {
            // Token จาก cookie
            token = req.cookies.token;
        }

        // ⚠️ ในทางปฏิบัติ ควรเก็บ SECRETKEY ไว้ใน .env
        const SECRETKEY = process.env.JWT_SECRET || "secret1234";

        if (!token) {
            return res.status(401).json({ 
                auth: false, 
                message: 'No token provided.' 
            });
        }

        jwt.verify(token, SECRETKEY, function (err, decoded) {
            if (err) {
                return res.status(401).json({ 
                    auth: false, 
                    message: 'Failed to authenticate token.' 
                });
            }

            // เก็บข้อมูล decoded token ใน req.user สำหรับใช้ใน routes อื่น
            req.user = decoded;
            next();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode
🔍 อธิบายโค้ด:
  • verifyToken() - Middleware function สำหรับตรวจสอบ token
  • jwt.verify() - ตรวจสอบความถูกต้องของ token
  • req.user - เก็บข้อมูลจาก decoded token
  • next() - เรียกใช้ middleware หรือ route ถัดไป

⚠️ คำเตือน: ในทางปฏิบัติ ควรเก็บ SECRETKEY ไว้ในไฟล์ .env หรือ environment variables เพื่อความปลอดภัยนะครับ


Section 4-3 - 🔐 การใช้งาน Middleware เพื่อป้องกัน Routes

🛡️ ป้องกัน Faculty Routes

แก้ไขไฟล์: routes/faculty.route.js

const express = require("express");
const router = express.Router();
const facultyController = require("../controllers/faculty.controller");
const VerifyToken = require('../middlewares/verify-token');

// Protected routes - ต้องมี token ถึงจะเข้าถึงได้
router.get("/", VerifyToken.verifyToken, facultyController.findAll);
router.get("/:id", VerifyToken.verifyToken, facultyController.findOne);
router.post("/", VerifyToken.verifyToken, facultyController.createOne);
router.put("/:id", VerifyToken.verifyToken, facultyController.update);
router.delete("/:id", VerifyToken.verifyToken, facultyController.delete);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

🛡️ ป้องกัน Student Routes

แก้ไขไฟล์: routes/student.route.js

const express = require("express");
const router = express.Router();
const studentController = require("../controllers/student.controller");
const VerifyToken = require('../middlewares/verify-token');

// Public route - ไม่ต้องมี token
router.post("/login", studentController.login);

// Protected routes - ต้องมี token ถึงจะเข้าถึงได้
router.get("/", VerifyToken.verifyToken, studentController.findAll);
router.get("/:id", VerifyToken.verifyToken, studentController.findOne);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

🧪 ทดสอบ Protected Routes

✅ ทดสอบด้วย Token (สำเร็จ)
GET http://localhost:5000/api/faculty
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Enter fullscreen mode Exit fullscreen mode
❌ ทดสอบโดยไม่มี Token (ล้มเหลว)
GET http://localhost:5000/api/faculty
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "auth": false,
  "message": "No token provided."
}
Enter fullscreen mode Exit fullscreen mode

Section 4-4 - 📝 ตัวอย่างโค้ดเต็ม

📄 ตัวอย่าง Student Controller (สมบูรณ์)

ไฟล์: controllers/student.controller.js

const db = require('../models')
const studentModel = db.students;
const jwt = require('jsonwebtoken');

// Get all students (Protected)
exports.findAll = async (req, res) => {
    try {
        const response = await studentModel.findAll()
        res.status(200).json({
            message: "get all student was successfully",
            payload: response
        })
    } catch (error) {
        res.status(500).json({
            message: error.message || "get all student was failed"
        })
    }
}

// Get student by id (Protected)
exports.findOne = async (req, res) => {
    try {
        const id = req.params.id
        const response = await studentModel.findOne({
            where: { std_id: id }
        })

        if (response) {
            res.status(200).json({
                message: "get one student was successfully",
                payload: response
            })
        } else {
            res.status(404).json({
                message: "Student not found"
            })
        }
    } catch (error) {
        res.status(500).json({
            message: error.message || "get one student was failed"
        })
    }
}

// Login (Public)
exports.login = async (req, res) => {
    try {
        const id = req.body.stdId;
        const pass = req.body.stdPass;

        const response = await studentModel.findOne({
            where: {
                std_id: id,
                std_pass: pass
            },
            raw: true
        })

        if (response) {
            const SECRETKEY = process.env.JWT_SECRET || "secret1234";

            const token = jwt.sign({
                std_id: response.stdId,
            }, SECRETKEY, {
                expiresIn: "1h",
            });

            delete response.stdPass;

            res.status(200).json({
                message: "login was successfully",
                payload: { ...response, token }
            })
        } else {
            res.status(401).json({
                message: "Invalid credentials"
            })
        }

    } catch (error) {
        res.status(500).json({
            message: error.message || "login student was failed"
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

📄 ตัวอย่าง Student Routes (สมบูรณ์)

ไฟล์: routes/student.route.js

const express = require("express");
const router = express.Router();
const studentController = require("../controllers/student.controller");
const VerifyToken = require('../middlewares/verify-token');

// Public routes
router.post("/login", studentController.login);

// Protected routes
router.get("/", VerifyToken.verifyToken, studentController.findAll);
router.get("/:id", VerifyToken.verifyToken, studentController.findOne);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Section 4-5 - 🔧 การใช้ Environment Variables

📦 ติดตั้ง dotenv

npm install dotenv
Enter fullscreen mode Exit fullscreen mode

📄 สร้างไฟล์ .env

ไฟล์: .env

JWT_SECRET=your_super_secret_key_here_change_this_in_production
DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=
DB_DATABASE=db_std
PORT=5000
Enter fullscreen mode Exit fullscreen mode

⚠️ สำคัญ: อย่าลืมเพิ่ม .env ใน .gitignore เพื่อไม่ให้ commit ไฟล์นี้ขึ้น GitHub นะครับ


🔧 แก้ไข server.js เพื่อใช้ dotenv

ไฟล์: server.js

require('dotenv').config(); // เพิ่มบรรทัดนี้ไว้ด้านบนสุด

const express = require('express')
const app = express();
const db = require("./models");

app.use(express.json())
app.use(express.urlencoded({ extended: true }))
const PORT = process.env.PORT || 5000

// ... routes และโค้ดอื่นๆ
Enter fullscreen mode Exit fullscreen mode

🔧 แก้ไข Middleware และ Controller

ไฟล์: middlewares/verify-token.js

const jwt = require('jsonwebtoken');

module.exports = {
    verifyToken(req, res, next) {
        // ... โค้ดเดิม
        const SECRETKEY = process.env.JWT_SECRET; // ใช้จาก .env
        // ... โค้ดเดิม
    }
}
Enter fullscreen mode Exit fullscreen mode

ไฟล์: controllers/student.controller.js

// ... โค้ดเดิม
const SECRETKEY = process.env.JWT_SECRET; // ใช้จาก .env
// ... โค้ดเดิม
Enter fullscreen mode Exit fullscreen mode

🎉 สรุป Module 4

เราได้เรียนรู้การสร้างระบบ Authentication และ Authorization ด้วย JWT ครบทั้ง:

ฟีเจอร์ คำอธิบาย
🔑 JWT Authentication สร้าง token สำหรับยืนยันตัวตน
🛡️ Middleware Protection ป้องกัน routes ด้วย middleware
🔒 Token Verification ตรวจสอบความถูกต้องของ token
🔐 Secure Routes แยก public และ protected routes
🌍 Environment Variables เก็บ secret keys อย่างปลอดภัย

📋 สรุป API Endpoints

Public Endpoints (ไม่ต้องมี Token)

Method Endpoint Description
POST /api/student/login Login และรับ token

Protected Endpoints (ต้องมี Token)

Method Endpoint Description
GET /api/faculty ดึงข้อมูล faculty ทั้งหมด
GET /api/faculty/:id ดึงข้อมูล faculty ตาม id
POST /api/faculty สร้าง faculty ใหม่
PUT /api/faculty/:id อัปเดตข้อมูล faculty
DELETE /api/faculty/:id ลบ faculty
GET /api/student ดึงข้อมูล student ทั้งหมด
GET /api/student/:id ดึงข้อมูล student ตาม id

🧪 ตัวอย่างการใช้งาน

1. Login เพื่อรับ Token

POST http://localhost:5000/api/student/login
Content-Type: application/json

{
  "stdId": "12345",
  "stdPass": "password123"
}
Enter fullscreen mode Exit fullscreen mode

2. ใช้ Token เพื่อเข้าถึง Protected Routes

GET http://localhost:5000/api/faculty
Authorization: Bearer <your_token_here>
Enter fullscreen mode Exit fullscreen mode

Reference

GitHub logo akkaraponph / basic-nodejs-express-sequelize-mysql

สร้าง API โดยใช้ JavaScript Node.js Express

หมายเหตุ Tutorial นี้ได้จัดทำขึ้นเมื่อ (April 4th, 2023) และได้รับการปรับปรุงเมื่อ (January 8th, 2026)

🚀 การสร้าง API โดยใช้ JavaScript Node.js Express

Node.js Express MySQL Sequelize

คู่มือการพัฒนา RESTful API แบบครบวงจรด้วย Node.js, Express, Sequelize และ MySQL

⬆ กลับไปด้านบน📚 สารบัญ


📖 คำนำ

เอกสารฉบับนี้เป็นการพัฒนา web service หรือ RESTful API โดยใช้ภาษา JavaScript Node.js ร่วมกับเฟรมเวิร์กและไลบรารี่ต่าง ๆ ได้แก่:

  • 🌐 Express HTTP Framework - สำหรับสร้าง web server
  • 🗄️ Sequelize - ORM สำหรับจัดการฐานข้อมูล
  • 💾 MySQL - ฐานข้อมูลเชิงสัมพันธ์
  • 🔧 POSTMAN - สำหรับทดสอบ API

📚 สารบัญ

Top comments (0)