background

Continue to last

  • Front-end to full stack starts here
  • [Advanced] KoA +Mysql full stack tour

Complete the preliminary understanding of the cloud server, as well as the configuration of Nginx, MySQL installation, table building operations and the use of Node.js framework Koa for simple data add, delete, change and check functions. However, for a more perfect website, it also needs a permission authentication (login, logout, registration, API authentication), and then JWT (JSONWebToken) written by Node to build token authentication module.

The target function

  • The login
  • registered
  • logout
  • Authentication for all apis
  // login
 const User = require('.. /controller/user');
 router.post('/user/login', User.login);
 router.post('/user/register', User.register);
 router.get('/user/loginout', User.loginOut);
Copy the code

Based on the Token

A token that is equivalent to a username and password and is capable of authentication

process

The client uses the user name and password to request login. 2. After receiving the request, the server verifies whether the login is successful. The client stores the Token after receiving it. 4. Sends the Token to the server each time it initiates a request. After receiving the request, the server verifies the Token. - Success: Returns the data required by the client. - Failure: returns the information about the authentication failureCopy the code

The flow chart

CryptoJS front-end encryption and decryption

Crypto-js is a pure javascript written encryption algorithm class library, can be very convenient in javascript MD5, SHA1, SHA2, SHA3, RIPemD-160 hash hash, Perform AES, DES, Rabbit, RC4, and Triple DES encryption and decryption.

Here the encryption of the password passed from the front end is compared with the password of the data store. Of course, the client can use the same public key encryption, and then the server can use the public key decryption and then do the comparison is consistent.

After crypto-js is installed, the code is as follows.

const CryptoJS = require('crypto-js');
/** * encryption */
function encrypt(word) {
  const key = CryptoJS.enc.Utf8.parse('yyq1234567890yyq');// 16-bit random public key
  const srcs = CryptoJS.enc.Utf8.parse(word);
  const encrypted = CryptoJS.AES.encrypt(srcs, key, {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
  });
  return encrypted.toString();
}

/** * decrypt */
function decrypt(word) {
  // 16 bits required
  const key = CryptoJS.enc.Utf8.parse('yyq1234567890yyq');// 16-bit random public key
  const srcs = CryptoJS.enc.Utf8.stringify(word);
  const decrypt = CryptoJS.AES.decrypt(srcs, key, {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
  });
  console.log(decrypt);
  return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}

module.exports = {
  encrypt,
  decrypt
};

Copy the code

JWT(Json Web Tokens)

There are many solutions for generating tokens, but I’m using one hereJWT.Json web token (JWT)Is an open jSON-based standard implemented to pass declarations between network application environments. JWT is made up of three pieces of information. Use these three pieces of information text. The links together make up the Jwt string. Something like this:XXX.XXX.XXXX

Generate private and public keys using OpenSSL

Key words: generate token TOekn; use private key encryption; use public key decryption to obtain user information

 openssl genrsa -out rsa_private_key.pem 1024
 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
Copy the code

Here’s the code

// Introduce module dependencies
const fs = require('fs');
const path = require('path');
// JsonWebToken needs to be installed
const jwt = require('jsonwebtoken');
// Create the token class
class Jwt {
 constructor(data) {
   // The token needs to carry the information such as the user ID
   this.data = data;
 }
 / / token is generated
 generateToken() {
   const data = this.data;
   const created = Date.now();
   // Private key encryption
   const cert = fs.readFileSync(path.join(__dirname, '.. /pem/rsa_private_key.pem')); // The private key can be generated by itself
   const token = jwt.sign(
     {
       data,
       exp: created + 60 * 30 * 1000
     },
     cert,
     { algorithm: 'RS256'});return token;
 }
 / / validation token
 verifyToken() {
   const token = this.data;
   const cert = fs.readFileSync(path.join(__dirname, '.. /pem/rsa_public_key.pem')); // Public keys can be generated by themselves
   let res;
   try {
     // Public key decryption
     const result = jwt.verify(token, cert, { algorithms: ['RS256'] }) || {};
     const { exp = 0 } = result;
     const current = Date.now();
     // Verify timeliness
     if(current <= exp) { res = result.data || {}; }}catch (e) {
     res = 'err';
   }
   returnres; }}module.exports = Jwt; 
Copy the code

Authentication for all apis

const passUrl = ['/user/login'.'/user/register'.'/ 404'.'/user/loginout'];
app.use(async(ctx, next) => {
  // The token check is required for all other requests
  if(! ~passUrl.findIndex(item= > ctx.request.url === item)) {
    const token = ctx.headers.token;
    if(! token) { ctx.body = {status: 403.msg: 'Token cannot be empty' };
    }
    const jwt = new JwtUtil(token);
    const result = jwt.verifyToken();
    // If the test passes next, otherwise return the login information is incorrect
    if (result == 'err'| |! result) { ctx.body = {status: 403.msg: 'Login has expired, please log in again' };
      return false;
    } else {
      // The user ID can be resolved
      console.log(result);
      // Query the Id and verify the token
      const res = await User.findOne({
        where: { id: Number(result) }
      });
      // Token does not exist or is inconsistent
      if(res.token ! == token || ! res.token) { ctx.body = {status: 403.msg: 'Login has expired, please log in again' };
        return false; }}}await next();
});
Copy the code

The login API

Determine if the user name and password are correct, generate and save the token, and return it to the client

/ / login
const login = async ctx => {
  const bodyData = ctx.request.body || {};
  const userName = bodyData.userName;
  const passWord = bodyData.passWord;
  if(! userName || ! passWord) { ctx.body = {code: 300.msg: 'Username and password cannot be empty! '
    };
    return false;
  }
  try {
    let result = await User.findAll({
      where: {
        userName: userName
      }
    });
    if (result.length) {
      result = result[0];
      console.log(result);
      // Use AES password decryption to determine whether the password is correct
      const aes = encryptionAndDecryption.encrypt(passWord);
      if (result.passWord === aes) {
        // The login succeeds and the token authentication is added
        const _id = result.id.toString();
        // Pass in the user ID and generate the token
        const jwt = new JwtUtil(_id);
        const token = jwt.generateToken();
        console.log('login id:' + _id);
        console.log('login token:' + token);
        // Store tokens in redis cache
        const updateRes = await User.update(
          {
            token
          },
          {
            where: {
              id: _id
            }
          }
        );
        // Return the token to the client
        ctx.body = { status: 200.msg: 'Successful landing'.token: token };
      } else {
        ctx.body = { status: 500.msg: 'Wrong account password' };
        return false; }}else {
      ctx.body = { status: 500.msg: 'Wrong account password'}; }}catch (error) {
    ctx.body = { status: 500.msg: error }; }};Copy the code

To register the API

Simple registration, determining whether the user name exists, then generating and saving the token and returning it to the client

/ / register
const register = async ctx => {
  const bodyData = ctx.request.body || {};
  const userName = bodyData.userName;
  const passWord = bodyData.passWord;
  if(! userName || ! passWord) { ctx.body = {code: 300.msg: 'Username and password cannot be empty! '
    };
    return false;
  }

  try {
    const result = await User.findAll({
      where: {
        userName: userName
      }
    });
    if (result.length) {
      ctx.body = {
        code: 300.msg: 'Username already exists'
      };
      return false;
    }

    // Update database
    const res = await User.create({
      userName,
      passWord: encryptionAndDecryption.encrypt(passWord)
    });
    // The login succeeds and the token authentication is added
    const _id = res.dataValues.id.toString();
    // Pass in the user ID and generate the token
    const jwt = new JwtUtil(_id);
    const token = jwt.generateToken();
    // Store the token
    await User.update(
      { token },
      {
        where: {
          id: _id
        }
      }
    );
    ctx.body = {
      code: 100.data: 'Created successfully'.token: token
    };
  } catch (err) {
    ctx.body = {
      code: 300.data: err }; }};Copy the code

Log out of the API

Delete token records stored on the server

const loginOut = async ctx => {
  const jwt = new JwtUtil(ctx.headers.token);
  const result = jwt.verifyToken();
  // Store the token
  const res = await User.update(
    { token: ' ' },
    {
      where: {
        id: result
      }
    }
  );
  console.log(res);

  ctx.body = {
    code: 100.msg: 'Logout successful'
  };
};
Copy the code

Database User table structure

const sequelize = require('.. /utils/sequelize');
const Sequelize = require('sequelize');
const moment = require('moment');
// Define the table structure
const user = sequelize.define(
  'user',
  {
    id: {
      type: Sequelize.INTEGER(11), // Set the field type
      primaryKey: true.// Set the primary key
      autoIncrement: true / / since the increase
    },
    userName: {
      type: Sequelize.STRING
    },
    passWord: {
      type: Sequelize.STRING
    },
    token: {
      type: Sequelize.TEXT,
      allowNull: true
    },
    createdAt: {
      type: Sequelize.DATE,
      defaultValue: Sequelize.NOW,
      get() {
        // this.getDataValue gets the current field value
        return moment(this.getDataValue('createdAt')).format('YYYY-MM-DD HH:mm'); }},updatedAt: {
      type: Sequelize.DATE,
      defaultValue: Sequelize.NOW,
      get() {
        return moment(this.getDataValue('updatedAt')).format('YYYY-MM-DD HH:mm'); }}}, {// Sequelize will automatically use the plural of the model name (the first parameter of define) as the table name. True cancels the default setting
    freezeTableName: true});module.exports = user;
Copy the code

Think about it more

In fact, you can add menu permissions. After the login interface is successfully accessed and the token is returned, the permission list is queried and returned to the client. Write another \auth route to get the list of permissions. Of course, this process still obtains user information through Token.

// The server returns the USER's permission CODE // for example: ['USER', // USER menu 'MY', // MY menu] // The client obtains the permission CODE returned by the server to judge the corresponding content renderingCopy the code

About the project

  • After writing a debug Node.js project and modifying the code, you need to close it manually frequently and then restart it again, which is very inconvenient. So it’s recommendedNode automatically restarts the tool Nodemon
    NPM install -g nodemon // Usage method //app.js entry file 8181 // The end number can also be used without nodemon app.js 8181Copy the code
  • There is also ES6 syntax and code formatting, which won’t be covered here. The installationbabelandeslintCan.

The last

Here mainly records the code process, some concepts are not detailed, reference as follows: JWT, crypto-js

Attach the code address https://gitee.com/wisdom_QQ/koa

After watching it, please like it!! After watching it, please like it!! After watching it, please like it!!