Jwt nodejs

Như vậy chúng ta đã kết nối được với MongoDB, tạo dữ liệu và tạo phần mềm trung gian để kiểm tra Xác thực và Cấp phép, phần tiếp theo mình sẽ hướng dẫn các bạn tạo các Bộ điều khiển (Tạo bộ điều khiển để xác thực, Bộ điều khiển để kiểm tra Cấp phép) và xác định các Tuyến cho

4. Kết quả

Mong rằng bài viết của mình giúp ích được phần nào cho các bạn. Cảm ơn các bạn. Hẹn gặp lại các bạn trong các bài viết tiếp theo nhé. ❤️

Ứng dụng Nodejs xác thực sử dụng JWT(Json Web Token) rất hữu ích khi bạn đang xây dựng một ứng dụng cho phép người dùng xác thực từ nhiều thiết bị (ứng dụng web, ứng dụng di động…)

Một ứng dụng sử dụng xác thực bằng mã thông báo hoạt động như thế nào

  • Người dùng đăng nhập vào hệ thống và sau khi xác nhận thành công, người dùng nhận được mã thông báo duy nhất và mã này có thời hạn sử dụng (ví dụ 15 phút)
  • Trong mỗi lần gọi API tiếp theo, người dùng phải đính kèm mã thông báo đính kèm trong yêu cầu để truy cập tài nguyên của hệ thống
  • Khi hết thời gian, người dùng phải đăng nhập lại để nhận mã thông báo mới

Bước cuối cùng gây ra nhiều khó khăn, chúng ta không thể bắt người dùng đăng nhập lại mỗi khi mã token bị hết hạn

Chúng ta có 2 cách để giải quyết vấn đề trên

  1. Tăng thời gian hết hạn của mã thông báo
  2. Sử dụng Làm mới mã thông báo để yêu cầu một mã thông báo mới

Trong bài viết này, mình hướng dẫn xây dựng một ứng dụng Nodejs có bước xác thực người dùng và sử dụng giải pháp Làm mới mã thông báo để xử lý trường hợp mã thông báo đã hết hạn

Chú thích. Code trong bài viết chỉ nên dùng để giải thích, không nên dùng trong các ứng dụng thực tế

Khởi tạo dự án

Chúng ta sẽ đi thẳng vào việc xây dựng mã ứng dụng. Mình sẽ giải thích trên các đoạn mã

Chúng ta cần tạo một thư mục mới, thư mục này sẽ chứa toàn bộ mã của dự án

Từ phần này về sau, các câu lệnh đều được chạy trong thư mục này

Sử dụng câu lệnh dưới đây để tạo mới một ứng dụng Nodejs

npm init --y

Câu lệnh trên sẽ tạo ra tệp

npm i --S express body-parser jsonwebtoken
7

Cài đặt các thư viện cần thiết

Chúng ta sẽ cần một vài thư viện để ứng dụng có thể chạy được. Chạy câu lệnh bên dưới để cài đặt chúng

npm i --S express body-parser jsonwebtoken

Các thư viện đã được cài đặt, gói tệp nội dung. json cũng đã được cập nhật, chúng ta sẽ truy cập phần tiếp theo

Gitignore

Bạn cần thêm tệp này để tránh các thư mục hoặc tệp định sẵn nhất được thêm vào Kho lưu trữ Git

You have to create new file. gitignore và thêm dòng dưới đây

node_modules/

Điều này có nghĩa là chúng ta sẽ không thêm thư mục node_modules vào git repo

Ok, tới lúc viết code rồi

Khởi tạo http server và các route cơ bản

Chúng ta sẽ sử dụng express để tạo một máy chủ http mới bằng Nodejs. Đây là tệp nội dung

npm i --S express body-parser jsonwebtoken
8

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const router = express.Router();
const config = require('./config');
const utils = require('./utils');
const tokenList = {};
const app = express();

router.get('/', (req, res) => {
  res.send('Ok');
});

/**
 * Đăng nhập
 * POST /login
 */
router.post('/login', (req, res) => {
  const postData = req.body;
  const user = {
    "email": postData.email,
    "name": postData.name
  }

  // Thực hiện việc kết nối cơ sở dữ liệu (hay tương tự) để kiểm tra thông tin username and password
  // Đăng nhập thành công, tạo mã token cho user
  const token = jwt.sign(user, config.secret, {
    expiresIn: config.tokenLife,
  });

  // Tạo một mã token khác - Refresh token
  const refreshToken = jwt.sign(user, config.refreshTokenSecret, {
    expiresIn: config.refreshTokenLife
  });

  // Lưu lại mã Refresh token, kèm thông tin của user để sau này sử dụng lại
  tokenList[refreshToken] = user;

  // Trả lại cho user thông tin mã token kèm theo mã Refresh token
  const response = {
    token,
    refreshToken,
  }

  res.json(response);
})

/**
 * Lấy mã token mới sử dụng Refresh token
 * POST /refresh_token
 */
router.post('/refresh_token', async (req, res) => {
  // User gửi mã Refresh token kèm theo trong body
  const { refreshToken } = req.body;

  // Kiểm tra Refresh token có được gửi kèm và mã này có tồn tại trên hệ thống hay không
  if ((refreshToken) && (refreshToken in tokenList)) {

    try {
      // Kiểm tra mã Refresh token
      await utils.verifyJwtToken(refreshToken, config.refreshTokenSecret);

      // Lấy lại thông tin user
      const user = tokenList[refreshToken];

      // Tạo mới mã token và trả lại cho user
      const token = jwt.sign(user, config.secret, {
        expiresIn: config.tokenLife,
      });
      const response = {
        token,
      }
      res.status(200).json(response);
    } catch (err) {
      console.error(err);
      res.status(403).json({
        message: 'Invalid refresh token',
      });
    }
  } else {
    res.status(400).json({
      message: 'Invalid request',
    });
  }
});

/**
 * Middleware xác thực người dùng dựa vào mã token
 * @param {*} req 
 * @param {*} res 
 * @param {*} next 
 */
const TokenCheckMiddleware = async (req, res, next) => {
  // Lấy thông tin mã token được đính kèm trong request
  const token = req.body.token || req.query.token || req.headers['x-access-token'];

  // decode token
  if (token) {
    // Xác thực mã token và kiểm tra thời gian hết hạn của mã
    try {
      const decoded = await utils.verifyJwtToken(token, config.secret);

      // Lưu thông tin giã mã được vào đối tượng req, dùng cho các xử lý ở sau
      req.decoded = decoded;
      next();
    } catch (err) {
      // Giải mã gặp lỗi: Không đúng, hết hạn...
      console.error(err);
      return res.status(401).json({
        message: 'Unauthorized access.',
      });
    }
  } else {
    // Không tìm thấy token trong request
    return res.status(403).send({
      message: 'No token provided.',
    });
  }
}

router.use(TokenCheckMiddleware);

router.get('/profile', (req, res) => {
  // all secured routes goes here
  res.json(req.decoded)
})

app.use(bodyParser.json());

app.use('/api', router);

app.listen(config.port || process.env.PORT || 3000);

Nội dung file utils. js

const jwt = require('jsonwebtoken');

module.exports = {
  verifyJwtToken: (token, secretKey) => {
    return new Promise((resolve, reject) => {
      jwt.verify(token, secretKey, (err, decoded) => {
        if (err) {
          return reject(err);
        }
        resolve(decoded);
      });
    });
  }
}

Tệp này cung cấp một chức năng để xác thực mã thông báo

Mình chuyển từ chức năng gọi lại thành Lời hứa để dễ sử dụng

This is internal dung file config. js

module.exports = {
  "secret": "s0me-secr3t-goes-here",
  "refreshTokenSecret": "some-s3cret-refre2h-token",
  "port": 3000,
  "tokenLife": 900, // 15 phút
  "refreshTokenLife": 86400 // một ngày
}

Mình sử dụng 2 chuỗi bí mật và 2 khoảng thời gian hết hạn khác nhau cho 2 loại token. Sau khi đã có 2 loại mã thông báo, tiền của mình hành động lưu lại thông tin Làm mới mã thông báo vào một biến. Use main token that doing key and value is user information

________số 8

Chú thích. Bạn nên sử dụng một nơi lưu trữ ổn định thay vì một bộ biến cục bộ trên sản phẩm, bạn có thể sử dụng Redis

Với route

npm i --S express body-parser jsonwebtoken
9 chúng ta sẽ lấy thông tin Làm mới mã thông báo từ trong phần thân máy khách gửi lên, nếu mã thông báo này tồn tại, chúng ta sẽ kiểm tra tính hợp lệ của nó (Có tồn tại trên hệ thống hay không, phải làm hệ thống sinh ra hay không)

Nếu nhận được mã thông báo Làm mới hợp lệ, chúng tôi sẽ tạo mã thông báo mới và gửi mã mới này về cho người dùng. Bằng cách này người dùng không cần phải đăng nhập lại

Phần mềm trung gian xác thực cho các API cần bảo vệ

Chúng ta cần có một phần mã luôn luôn phải được thực thi để kiểm tra mã thông báo mà khách hàng gửi kèm theo các yêu cầu có hợp lệ hay không

Như bạn đã thấy, mình đang tạo ra một hàm để làm công việc này

npm i --S express body-parser jsonwebtoken
0

Thực hiện kiểm tra mã thông báo một cách đơn giản. Bằng phần mềm trung gian này, mọi tuyến đường được đăng ký sau đó đều được bảo vệ, mọi yêu cầu đều cần có mã thông báo hợp lệ để có thể truy cập được các tài nguyên của hệ thống

Ứng dụng thử nghiệm

Đến lúc chúng ta sẽ xem ứng dụng hoạt động như thế nào

Khởi động ứng dụng bằng câu lệnh

npm i --S express body-parser jsonwebtoken
1

Mở công cụ làm việc với API mà bạn yêu thích và thử nghiệm, mình hay sử dụng Postman

mẹo. Mình sẽ sử dụng curl để mô tả các yêu cầu ở phần bên dưới, các bạn có thể sao chép đoạn yêu cầu bằng cách cuộn tròn và nhập vào Postman

Đầu tiên sẽ là đăng nhập API

node_modules/
0

npm i --S express body-parser jsonwebtoken
3

Kết quả nhận được sẽ tương tự

npm i --S express body-parser jsonwebtoken
0

Bây giờ, hãy sao chép nội dung của

node_modules/
1, nó sẽ được dùng để xác thực khi gọi các api cần xác thực

Như đoạn mã của phần mềm trung gian

node_modules/
2 điều này có nghĩa là chúng ta có thể gửi mã thông báo đính kèm trong phần thân, chuỗi truy vấn hoặc tiêu đề của yêu cầu, mình sẽ lấy ví dụ mã thông báo được gửi kèm trong tiêu đề của yêu cầu. (Sử dụng token giá trị tại thời điểm viết bài)

npm i --S express body-parser jsonwebtoken
1

Kết quả, thông tin các nhân của mình (kèm theo một vài thông tin của mã jwt)

npm i --S express body-parser jsonwebtoken
2

Có được thông tin này vì ở bước trung gian, sau khi xác thực và mã thông báo giải mã, mình đã gán thông tin giải mã được cho đối tượng yêu cầu, trong hồ sơ tuyến đường chỉ cần lấy thông tin đó để trả về

Nếu mình yêu cầu POST /api/profile mà không có thông tin token thì sẽ nhận được trạng thái http là 403, kèm theo tin nhắn

npm i --S express body-parser jsonwebtoken
3

nếu mã thông báo không đúng hoặc bị hết hạn, trạng thái http là 401, kèm theo tin nhắn

npm i --S express body-parser jsonwebtoken
4

Sau đó, khi mã thông báo hết hạn (các bạn có thể rút ngắn thời gian sống của mã thông báo để có thể kiểm tra dễ dàng), chúng tôi sẽ gọi API sử dụng Làm mới mã thông báo để lấy mã thông báo mới

npm i --S express body-parser jsonwebtoken
5

kết quả là chúng tôi nhận được mã thông báo mới hợp lệ

npm i --S express body-parser jsonwebtoken
6

Kết luận

Xây dựng một ứng dụng web bằng Nodejs thì công việc phải đảm bảo các cơ chế xác thực người dùng là không thể thiếu. Bạn nên xây dựng kèm theo cơ chế làm mã thông báo xác thực mới, việc này giúp cho ứng dụng phía client có thể hoạt động một cách liền mạch

Mình đã trình bày những điều cơ bản nhất, hy vọng bạn có thể vận dụng được những gì trong dự án của mình