Preface
Why do you have to write this blog? Because this incident makes me feel good about the balls. Don't ask the rest, I won't say anything else. Because I don’t want to (don’t bother) to draw the flow chart.
Development architecture
Front-end page: Vue
Network request: Axios; Method: vue add axios
Cache Scheme
Global variable: Vuex
Local Cache: LocalStorage
Technology dependency
you guess?
background
The company develops a web page embedded in the App, and uses the old routine in terms of security: the App passes parameters to the front-end (including signatures) through the URL, and the front-end passes the parameters to the H5 back-end to verify the signature. After the matter is done, the front-end decides whether the user is legal. In addition, N JS methods front-ends are defined to determine whether they are called by Android or Apple based on fixed GET parameters.
Preliminary concept
The preliminary idea about the token design plan is as follows: when you enter the first time, get the token, and the backend checks whether the signature is passed. If you do not pass, please enter the page from the legal channel and do not disappear.
Otherwise, the user can continue to follow-up operations until the backend returns the token expiration specific status code and returns the frontend calls the JS method to re-get the URL parameters and request the token when the user is unconscious. After the matter is finished, the user's request operation will continue. (To avoid users using old tokens to manipulate data elsewhere, each time they get the token, they re-get and verify from the App, instead of refreshing and returning the new token in the interface)
Exciting things
There is no version control when defining URL parameters in the first phase, resulting in the new JS method iterative version added to the front-end page called the unknown method page without any response; I don’t know how many periods of the data it was...
In order to avoid the 1 request change 3 requests caused by token expiration during the request process, the asynchronous method of token time needs to be checked before each request (if the token expires, getToken is called to get the new token and store it locally) causing block nesting.
I won’t talk about N methods later...
Upgrade ideas
Let’s not talk about the version or something. For this token issue, I can’t copy and paste every time I add a new request, right? It can annoy people! Then I can only judge the token timeliness before the axios request.
Go straight to the topic
Function declaration
getToken: Get the stored token from local
checkToken: Check the token time, if the failed call to refreshToken function is successful, it will be stored locally, otherwise the error reason will be returned
refreshToken: Call JS method to get signature parameters from the App and request token again
Things to note
When the token expires during the checkToken process, first remove the local expired token cache data.
/* eslint-disable no-console */ /* eslint-disable no-unused-vars */ "use strict"; import Vue from 'vue'; import axios from "axios"; import { getToken } from '../utils/' import { checkToken, refreshToken, clearCache } from "../utils/"; // Full config: /axios/axios#request-config // = || || ''; // ['Authorization'] = AUTH_TOKEN; // ['Content-Type'] = 'application/x-www-form-urlencoded'; ["Content-Type"] = "application/json"; let cancel, promiseArr = {}; let config = { baseURL: .VUE_APP_BASE_URL, timeout: 8 * 1000, // Timeout withCredentials: true, // Check cross-site Access-Control }; const _axios = (config); _axios.( function (config) { // Do something before request is sent let token = getToken(); // alert("token1:" + token); // When starting a request, cancel the same request currently in progress if (promiseArr[]) { promiseArr[]("Please wait"); promiseArr[] = cancel; } else { promiseArr[] = cancel; } if (token) { return checkToken(null) .then((result) => { // ("refreshToken result:", result); if (result === true) { token = getToken() // alert("token2:" + token); ["authorization"] = token; return config; } else { return (Error(result)) } }).catch((err) => { // Terminate this request return (err); }); } return config; }, function (error) { // Do something with request error return (error); } ); // Add a response interceptor _axios.( function (response) { // Do something with response data let { status, statusText, data } = response; if (err_check(status, statusText, data) && data) { // var randomColor = `rgba(${parseInt(() * 255)},${parseInt( // () * 255 // )},${parseInt(() * 255)})`; // ( // "%c┍------------------------------------------------------------------┑", // `color:${randomColor};` // ); // ("| Request address: ", ); // ("| Request parameters: ", ); // ("| Return data: ", ); // ( // "%c┕------------------------------------------------------------------┙", // `color:${randomColor};` // ); if ( === "0001") { clearCache() var config = ; var url = ; url = ("/apis", "").replace(.VUE_APP_BASE_URL, "") = url; // alert((config)) return refreshToken(null) .then((result) => { // ("refreshToken result:", result); if (result == true) { let token = getToken() if (token) { ["authorization"] = token; } return axios(config) .then((result) => { let { status, statusText, data } = result; // ('Interface quadratic request result:', result); if (err_check(status, statusText, data) && data) { return (data) } else { return (Error()); } }).catch((err) => { // ('Interface secondary request err:' + err); return (err); }); } else { // alert("result:" + result) return (Error()) } }).catch((err) => { // Terminate this request // alert("Terminate this request:" + ) // ("refreshToken err:", err); return (err); }); } else { return (data); } } else { return (Error(statusText)); } // return response; }, function (error) { // Do something with response error // ("error", error); return (error); } ); // eslint-disable-next-line no-unused-vars const err_check = (code, message, data) => { if (code == 200) { return true; } return false; }; = function (Vue, options) { = _axios; = _axios; (, { axios: { get() { return _axios; } }, $axios: { get() { return _axios; } }, }); }; (Plugin) export default Plugin;
Supplementary knowledge:vue+ axios+token encapsulate axios encapsulate interface url with token request, token invalid refresh
1. Encapsulate axios
import axios from 'axios' import qs from "qs" const TIME_OUT_MS = 60 * 1000 // Default request timeout time// = 'http://localhost:8080'; // http request interceptor( config => { if ($("access_token")) { // Determine whether token exists. If it exists, add tokens for each http header ='Bearer '+ $("access_token"); } return config; }, err => { return (err); }); // http response interceptor( response => { return response; }, error => { ("response error :"+error); if () { switch () { case 401: ("token expired"); var config = ; refresh(config); return; } } return (error) // Return the error message returned by the interface }); /* *Refresh token */ function refresh(config){ var refreshToken = $("refresh_token"); var grant_type = "refresh_token"; axios({ method: 'post', url: '/oauth/token', data: handleParams({"grant_type":grant_type,"refresh_token":refreshToken}), timeout: TIME_OUT_MS, headers: {} }).then( (result) => { if(.access_token){ //Resave token $("access_token",.access_token); $("refresh_token",.refresh_token); //Re-execution is required axios(config); }else{ //this.$('goto', 'login'); (); } } ).catch((error) => { //this.$('goto','login'); (); }); } /* * @param response Returns the data list */ function handleResults (response) { var result = { success: false, message: '', status: [], errorCode: '', data: {} } if ( == '200') { = ; = ; = true; } return result } // function handleUrl (url) { // //url = BASE_URL + url // url =root +url; // // BASE_URL is the ip prefix of the interface, such as http:10.100.1.1:8989/// return url // } /* * @param data parameter list * @return */ function handleParams (data) { return (data); } export default { /* * @param url * @param data * @param response The callback function when the request is successful * @param exception callback function */ post (url, data, response, exception) { axios({ method: 'post', //url: handleUrl(url), url: url, data: handleParams(data), timeout: TIME_OUT_MS, headers: { //'Content-Type': 'application/json; charset=UTF-8' } }).then( (result) => { response(handleResults(result)) } ).catch( (error) => { if (exception) { exception(error) } else { (error) } } ) }, /* * get request * @param url * @param response The callback function when the request is successful * @param exception callback function */ get (url,data, response, exception) { axios({ method: 'get', url: url, params:data, timeout: TIME_OUT_MS, headers: { 'Content-Type': 'application/json; charset=UTF-8' } }).then( (result) => { response(handleResults(result)) } ).catch( (error) => { ("error"+response); if (exception) { exception(error) } else { (error) } } ) } }
2. Configure axios cross-domain and request baseUrl
-->
' 'use strict' // Template version: 1.3.1 // see /webpack for documentation. const path = require('path') //Introduce cross-domain configurationvar proxyConfig = require('./proxyConfig') = { dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: '/', //proxyTable: {}, //The default cross-domain configuration is empty proxyTable: , // Various Dev Server settings host: 'localhost', // can be overwritten by port: 8886, // can be overwritten by , if port is in use, a free one will be determined autoOpenBrowser: false, errorOverlay: true, notifyOnErrors: true, poll: false, // /configuration/dev-server/#devserver-watchoptions- /** * Source Maps */ // /configuration/devtool/#development devtool: 'cheap-module-eval-source-map', // If you have problems debugging vue-files in devtools, // set this to false - it *may* help // /en/#cachebusting cacheBusting: true, cssSourceMap: true }, build: { // Template for index: (__dirname, '../dist/'), // Paths assetsRoot: (__dirname, '../dist'), assetsSubDirectory: 'static', // When the project name changes, it needs to be changed here. Originally assetsPublicPath: '.' assetsPublicPath: './', /** * Source Maps */ productionSourceMap: true, // /configuration/devtool/#production devtool: '#source-map', // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: .npm_config_report } }
Create a file in the directory File
={ proxy:{ '/':{ //Map localhost:8081 to /apis target:'http://localhost:8080',//Interface address changeOrigin: true,// If the interface crosses the domain, this parameter configuration is required secure:false, //If the interface is an HTTPS interface, it needs to be set to true pathRewrite:{ '^/':'' } } } }
3. Encapsulate API request Url
export default { oauth: { login: '/oauth/token', // Log in logout: '/oauth/logout' // // quit }, user: { addUser: '/user/add', updateUser: '/user/update', getUser:'/user/', //+ Id exists:'/exists/', // +id enable:'/enable/', // +id disable:'/disable/', // +id delete:'/delete/', //+id password:'/password ', query:'/query' } }
IV. Introduce
import http from './plugins/' import ports from './plugins/ports' = http = ports
V. Use
Used in
login() { (,{username:, password:,grant_type:'password'}, res => { if () { // Return to the correct processing Page jump this.$('goto', 'edit'); } else { // Return error handling //alert("waiting for processing"); } },err =>{ //("Processing"+); if(=='400'){ //Incorrect display of username or password this.$(); this.$(); } }) }
The above example of Vue axios obtaining token temporary token encapsulation is all the content I share with you. I hope you can give you a reference and I hope you can support me more.