preface

  • Why use tokens

HTTP is a stateless protocol, meaning that HTTP cannot store information about clients and cannot distinguish between each request.

Imagine A scenario where A and B modify individual articles at the same time. The server receives two POST requests at the same time, but the browser does not know which one is A and which one is B. An identifier (token) is needed to mark A string of information and carry it with the request

  • What is the token

A Token is a string of characters generated by the server as a Token requested by the client. When logged in for the first time, the server distributes the Tonken string to the client. For subsequent requests, the client only needs to bring this Token, and the server can know that it is the user’s access.

Personal understanding is a string of personal information encrypted by the server, such as this one:

acess_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZWRhMTBjOTI0NThmNDAwMmFjZDEyMTAiLCJuaWNrX25hbWUiOiLlsI_osaoxOTk2IiwiY3J lYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOTozMDo0OS42MThaIiwidXBkYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOToyOToyMC4wNzlaIiwiaWF0IjoxNTk xMzQ5NDY4LCJleHAiOjE1OTE5NTQyNjh9.GmUJRXHed7M1xJyPaFFgaQKJoS-w8-l3N_PQFPiwwTE
Copy the code

The server decrypts with a secret key to obtain information about the current requester

Technology stack

  • Front end: VUE + SSR
  • Back-end: Egg (a Node framework) + TS
  • Database: Redis + Mongo
  • Deploy: Docker
  • Construction: Jenkins

The blog is almost complete and SSR rendering is used

This article is mainly about token verification, and the rest will not be discussed

Token verification design

My blog does not do token validation for GET requests. Other requests that modify resources, such as POST and PUT, do token validation

It can be disassembled into the following three steps

  • After a client user logs in, the server generates a token based on the user information and stores the token persistently on the client
  • Client request with token
  • The server authenticates the token. If the token fails, the server returns an error status

1. Front-end (vue.js)

Use the AXIos library, and plug the token into the request header in the Request interceptor, and globally prompt the error status code in the Response interceptor

After a successful login, the token is written to the browser cache for each request

import Vue from 'vue';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';

axios.interceptors.request.use(
  async (config: AxiosRequestConfig) => {
    const acess_token = await Vue.prototype.$getCacheData('acess_token'); Read token in cache
    if (acess_token) {
      config.headers.acess_token = acess_token;
    }
    return config;
  },
  (err: any) => Promise.reject(err),
);

axios.interceptors.response.use(
  (response: AxiosResponse) => {
    if (response.data.ret === 200) {
      return response;
    } else {
      Vue.prototype.$global_fail(response.data.content);
      return Promise.reject(response);
    }
  },
  (err: any) => {
    console.log(err);
    if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1) {
      Vue.prototype.$global_error('Request timed out, please contact administrator');
    }
    if (err.response) {
      Vue.prototype.$global_error(decodeURI(err.response.data.msg || err.response.data.message));
    }
    returnPromise.reject(err); });Copy the code

For front-end caching, here I recommend a localForage database, which is useful

LocalForage is a JavaScript library that can store many types of data, not just strings. LocalForage has an elegant downgrade strategy and uses localStorage if the browser does not support IndexedDB or WebSQL.

Note, however, that its operations are all asynchronous, and you can encapsulate it to make it synchronous

import Vue from 'vue';
import localForage from 'localforage';

Vue.prototype.$setCacheData = async (key: string, data: any): Promise<void> => await localForage.setItem(key, data);
Vue.prototype.$getCacheData = async (key: string): Promise<string | null> => await localForage.getItem(key) || null;
Vue.prototype.$clearCache() = = >localForage.clear();
Copy the code

2. Back end (egg.js)

  • To generate the token

Users log in and use jsonWebToken to generate tokens

For details about jsonWebToken, please click node- JsonWebToken

The use of encryption and decryption is also relatively simple. The UserService method is directly provided, where secret is the secret key and the expiration time of the token can be set

# /app/service/user.ts
import * as jwt from 'jsonwebtoken';

export default class UserService extends Service {

  private secret = 'Hello__World'; # the secret key
  async createToken(user: User): Promise<string> {
    const payload = {
      _id: user._id,
      nick_name: user.nick_name,
      created_time: user.created_time,
      updated_time: user.updated_time,
    };
    return jwt.sign(payload, this.secret, { expiresIn: '7d' }); # Expiration time
  }

  checkToken(token: string): User {
    try {
      Decrypt token according to secret key
      return jwt.verify(token, this.secret);
    } catch (e) {
      throw 'Invalid token'; }}}Copy the code

Token authentication is performed in the middleware. If the token authentication fails, it is returned directly

  • Middleware validation

Turn on the Verify middleware and validate only specific POST requests:

Enabling middleware

# /config/config.default.ts
config.middleware = ['verify'];
config.verify = {
  enable: true.Verify only POST requests
  match(ctx) {
    return ctx.request.method === 'POST'; }};Copy the code

The checkToken method is called in Verify to verify the token

# /app/middleware/verify.ts
module.exports = () => {
  return async (ctx, next) => {
    if (ctx.path.startsWith('/api/user/login') || ctx.path.startsWith('/api/user/sendCode') || ctx.path.startsWith('/api/user/register')) {
      return await next();
    }
    try {
      const acess_token: string = ctx.request.header.acess_token;
      if(! acess_token) { throw'Please log in';
      } else {
        await ctx.service.user.checkToken(acess_token); # authentication token
        return await next();
      }
    } catch (e) {
      # token fails to validate and returns a custom status code
      console.log(e);
      ctx.body = {
        ret: 304,
        content: `${e}`}; }}; };Copy the code

This is the end of token verification


END