Authentication trong NodeJS

A. Giới thiệu Authentication trong NodeJS

Authentication trong NodeJS là bài hướng dẫn bạn cách triển khai chức năng liên quan đến người dùng như đăng nhập, kiểm tra đăng nhập, mã khóa mật khẩu …

gioi-thieu-authentication-trong-nodejs

Tại sao có authentication?

Bởi vì trong website, có những chức năng mà người dùng phải đăng nhập mới có thể sử dụng, ví dụ chức năng đặt hàng, quản lý sản phẩm, đổi mật khẩu, hay download tài liệu quan trọng… Authentication là thuật ngữ nói về việc tổ chức năng nhập, đăng ký, kiểm tra đăng nhập, đăng xuất… của người dùng.

Anthentication trong NodeJS hoạt động như thế nào?

hoat-dong-cua-autentication
Tổ chức hoạt động Autentication

B. Chuẩn bị học Authentication trong NodeJS

1. Tạo database và table

Bạn cần có 1 table chứa thông tin của người truy cập, table sẽ có các các cột như username, password, email, họ tên…để lưu thông tin người.dùng. Dữ liệu trong table này được dùng để so sánh với thông tin mà người dùng gửi lên, từ đó bạn sẽ biết được họ nhập thông tin có đúng không.

Thực hiện: Mời bạn tạo table có tên users trong database shop như đã thực hiện trong bài Sử dụng Restful API NodeJS , nếu đã tạo table users rồi thì thôi khỏi tạo lại, dùng nó để thực hiện bài này này luôn nhé.

2. Cài module express và express-generator

Nếu máy của bạn đã cài rồi thì thôi bỏ qua, còn chưa thì mở command line lên và cài nhé.

Thực hiện: Mở command line rồi gõ  lệnh npm install -g express . Tiếp theo gõ lệnh  npm -g install express-generator

3. Tạo project và cài module để thực tập

a. Tạo project: Mở command line rồi chạy lệnh để tạo project (AuthenNodeJS là tên folder project, bạn đặt tên khác cũng được)

express --view=ejs AuthenNodeJS

Bạn sẽ thấy folder AuthenNodeJS xuất hiện các folder và các file trong đó.

b. Cài module: Chuyển vào folder mới tạo và chạy các lệnh cài đặt module cần thiết.

npm install
npm install mysql

4. Tạo model kết nối database

– Tạo folder models trong project
– Tạo file file models/database.js

var mysql = require('mysql'); 
var db = mysql.createConnection({   
    host: 'localhost',     
    user: 'root',     
    password: '',     
    database: 'shop' 
});  
db.connect(function(err) {    
   if (err) throw err;    
   console.log('Da ket noi database'); 
}); 
module.exports = db; //lệnh exports để xuất (public) ra, cho bên ngoài module có thể dùng được db

C. Authentication trong NodeJS

1. Đăng ký thành viên và mã hóa mật khẩu

a. Tạo route

– Mở app.js và thêm code khai báo route cho controller

var tvRouter = require('./routes/thanhvien');
app.use('/thanhvien', tvRouter);

– Tạo file routes/thanhvien.js và code cho controller, nhúng module kết nối db và route cho chức năng đăng ký

var express = require('express');
var router = express.Router();
var db=require('../models/database'); 
router.get('/dangky', function(req, res) {
     res.render("dangky.ejs");
});
module.exports = router;

b Tạo view

– Tạo file views/dangky.ejs và nhập chữ Đăng ký thành viên

– Test: http://localhost:3000/thanhvien/dangky nếu thấy chữ vừa nhập là chính xác

– Code view dangky.ejs để hịiện form

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
<div class="container">
<form action="/thanhvien/luu" method="POST" class="col-6 border border-dark rounded p-3 m-auto" >
    <div class="form-group mb-3">
        <label for="un">Username</label>
        <input type="text" class="form-control bg-info" name="username" id="un">
    </div>
    <div class="form-group mb-3">
        <label for="pass">Mật khẩu</label>
        <input type="password" class="form-control bg-primary" name="password" id="pass">
    </div>
    <div class="form-group mb-3">
        <label for="email">Email</label>
        <input type="email" class="form-control bg-warning" name="email" id="email">
    </div>

    <div class="form-group mt-3 ">       
        <button type="submit" class="btn btn-danger px-4  py-2 text-white"> ĐĂNG KÝ</button>
    </div>
</form>    
</div>

d. Lưu thông tin user

– Định nghĩa route để nhận dữ liệu từ form trong router thanhvien
router.post('/luu', function(req, res) {
    let u = req.body.username;
    let p = req.body.password;
    let em = req.body.email;  
    let user_info ={username: u, password:p, email:em};  
    //let user_info = {};
    //user_info.username = u;
    //user_info.password = p;
    //user_info.email = em; 
    let sql = 'INSERT INTO users SET ?';
    db.query(sql, user_info);
    res.redirect("/thanhvien/camon");
})

e. Test đăng ký

1. Trong trình duyệt gõ http://localhost:3000/thanhvien/dangky , sẽ thấy form hiện ra

2. Nhập thông tin và submit form

authentication-trong-nodejs_form_register

3. Sau khi submit form sẽ thấy thông tin user trong form được lưu vào database

f. Mã hóa mật khẩu của user

Mật khẩu của user lưu trong database phải được mã hóa để đảm bảo an toàn cho thông tin của user . Để thực hiện , bạn cần cài thêm module bcryot để sử dụng.

1. Cài module bscript: npm install bcrypt
2. Code lại hàm luu trong router thanhvien

router.post('/luu', function(req, res) {
    let u = req.body.username;
    let p = req.body.password;
    let em = req.body.email;  

    const bcrypt = require("bcrypt");        
    var salt = bcrypt.genSaltSync(10);
    var pass_mahoa = bcrypt.hashSync(p, salt);

    let user_info ={username: u, password:pass_mahoa, email:em};  
    let sql = 'INSERT INTO users SET ?';
    db.query(sql, user_info);
    res.redirect("/thanhvien/camon");
})

Test: Giờ thì đăng ký lại, bạn sẽ thấy mật khẩu được mã hóa.

2. Đăng nhập và xử lý đăng nhập

a. Tạo route

– Code trong file routes/thanhvien.js để tạo hàm dangnhap

router.get('/dangnhap', function(req, res) {
     res.render("dangnhap.ejs");
});

b Tạo view

– Tạo file views/dangnhap.ejs và nhập chữ Form đăng nhập

– Test: http://localhost:3000/thanhvien/dangnhap nếu thấy chữ vừa nhập là chính xác

– Code view dangnhap.ejs để hịiện form

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
<div class="container">
<form action="/thanhvien/dangnhap_" method="POST" class="col-6 border border-dark rounded p-3 m-auto" >
    <div class="form-group mb-3">
        <label for="un">Username</label>
        <input type="text" class="form-control bg-info" name="username" id="un">
    </div>
    <div class="form-group mb-3">
        <label for="pass">Mật khẩu</label>
        <input type="password" class="form-control bg-primary" name="password" id="pass">
    </div>
    <div class="form-group mt-3 ">       
        <button type="submit" class="btn btn-danger px-4  py-2 text-white"> ĐĂNG NHẬP</button>
    </div>
</form>    
</div>

d. Xử lý đăng nhập

– Định nghĩa route dangnhap_ để kiễm tra username, pass
router.post('/dangnhap_', async function(req, res) {
    let u = req.body.username;
    let p = req.body.password;
    let sql = 'SELECT * FROM users WHERE username = ?';
    db.query(sql, [u] , (err, rows) => {   
        if (rows.length<=0) { 
            res.redirect("/thanhvien/dangnhap"); 
            return;
        }
        let user = rows[0];        
        let pass_fromdb = user.password;        
        const bcrypt = require("bcrypt");        
        var kq = bcrypt.compareSync(p, pass_fromdb);
        if (kq){ 
            console.log("OK");                    
            res.redirect("/thanhvien/thanhcong");
        }   
        else {
            console.log("Not OK");
            res.redirect("/thanhvien/dangnhap");
        }
    });   
});

e. Test đăng nhập

1. Trong trình duyệt gõ http://localhost:3000/thanhvien/dangnhap , sẽ thấy form hiện ra
2. Nhập thông tin và submit form
3. Sau khi submit form , nếu gõ username , pass sai sẽ thấy form đăng nhập hiện lại, gõ user pass đúng sẽ được chuyển đến route thanhcong

e. Lưu biến session khi đăng nhập thành công

Để lưu biến session ghi nhận user đã đăng nhập thành công, bạn sẽ dùng module express-session

1. Cài module express-session: npm install express-session

2. Trong file app.js, code để nạp module :

var session = require('express-session');

3. Trong file app.js, viết code cấu hình module session (trên lênh app.use(‘/thanhvien’, tvRouter) )

app.use(session({
  secret: 'abcdefg',
  resave: true,
  saveUninitialized: true,
  cookie: { maxAge: 60000 }
}));

4. Code lại trong hàm dangnhap_ của routes/thanhvien:

router.post('/dangnhap_', async function(req, res) {
    let u = req.body.username;
    let p = req.body.password;
    let sql = 'SELECT * FROM users WHERE username = ?';
    db.query(sql, [u] , (err, rows) => {   
        if (rows.length<=0) { res.redirect("/thanhvien/dangnhap"); return;}
        let user = rows[0];        
        let pass_fromdb = user.password;        
        const bcrypt = require("bcrypt");        
        var kq = bcrypt.compareSync(p, pass_fromdb);
        if (kq){ 
            console.log("OK");   
            var sess = req.session;  //initialize session variable
            sess.daDangNhap = true;
            sess.username = user.username;                     
            res.redirect("/thanhvien/thanhcong");
        }   
        else {
            console.log("Not OK");
            res.redirect("/thanhvien/dangnhap");
        }
    });   
});

4. Kiểm tra đăng nhập

– Trong routes/thanhvien.js, định nghĩa route download

router.get('/download', function(req, res) {
    res.render("download.ejs");
});

– Tạo views/download.ejs bà nhập text

<h4>Đây là trang tải phần mềm, chỉ dành cho thành viên đã đăng nhập</h4>

– Test: http://localhost:3000/thanhvien/download , nếu thấy text vừa nhập text là chính xác

– Code kiểm trang đăng nhập: code bổ sung trong hàm download để được như sau:

router.get('/download', function(req, res) {
    if (req.session.daDangNhap) {
        res.render("download.ejs",{un:req.session.username});
    }
    else {       
        res.redirect("/thanhvien/dangnhap");
    }
});

– Test: nếu chưa đăng nhập, khi xem http://localhost:3000/thanhvien/download sẽ bị chuyển sang trang dangnhap, nếu đã đăng nhập thì mới xem được.

5. Trở lại trang trước

– Trong các chức năng bắt user phải đăng nhập, ghi vào session địa chỉ của trang hiện hành (trang cũ)

– Trong chức năng xử lý đăng nhập, nếu thấy địa chỉ trang cũ thì quay lại trang cũ

Thực hiện như sau:

– Trong hàm download, code bổ sung để được như sau:

 router.get('/download', function(req, res) {
    if (req.session.daDangNhap) {
        res.render("download.ejs",{un:req.session.username});
    }
    else { 
        req.session.back="/thanhvien/download";
        res.redirect("/thanhvien/dangnhap");
    }
});

– Trong hàm dangnhap_, code bổ sung để được như sau:

router.post('/dangnhap_', async function(req, res) {
    let u = req.body.username;
    let p = req.body.password;
    let sql = 'SELECT * FROM users WHERE username = ?';
    db.query(sql, [u] , (err, rows) => {   
        if (rows.length<=0) { res.redirect("/thanhvien/dangnhap"); return;}
        let user = rows[0];        
        let pass_fromdb = user.password;        
        const bcrypt = require("bcrypt");        
        var kq = bcrypt.compareSync(p, pass_fromdb);
        if (kq){ 
            console.log("OK");   
            var sess = req.session;  //initialize session variable
            sess.daDangNhap = true;
            sess.username = user.username;                     
            //res.redirect("/thanhvien/thanhcong");
            if (sess.back){ 
                console.log(sess.back);
                res.redirect(sess.back);
            }
            else {
                res.redirect("/thanhvien/thanhcong");
            }
        }   
        else {
            console.log("Not OK");
            res.redirect("/thanhvien/dangnhap");
        }
    });   
});

Test: Xem trang download, nếu chưa đăng nhập sẽ được chuyển đến trang dangnhap, đăng nhập xong sẽ được chuyển ngược trở lại trang download

5. Thoát

Trong routes/thanhvien, tạo route thoat

router.get('/thoat', function(req, res) {
    req.session.destroy();
    res.redirect("/thanhvien/dangnhap");
});

Mời bạn thực hiện thêm: chức năng đổi pass, bổ sung thêm cho form đăng ký họ tên, sở thích, thêm nhãn cho các form đăng nhập, đăng ký, hiện text thông báo trong from đăng nhập nếu username nhập không có…