The vue project stores the token in (vuex) store and localstorage


Read carefully, think over and over again, Xiaobai will get started! If you have any questions, please leave a message❤

1. Preparation and token

1. Preparation
  • Understand
    the introduction of (session, cookie) token Token: Token frequently requests data from the server on the client side, and the server frequently queries the database for user names and passwords and compares them to determine whether the user names and passwords are correct or not, and make corresponding prompts. In this context, Token came into being.
    The token is a string of strings generated on the server to serve as a token for the client to request. If the front end uses the username/password to request authentication from the server and the server authentication succeeds, then the server will return the Token to the front end. The front end can bring Token to prove its legal status every time it requests. If this Token is persistent on the server (such as stored in a database), then it is a permanent identity token (unless a validity period is set).


  • Token advantages Token is completely managed by the application, so it can avoid the same-origin policy.
    Token can avoid CSRF attacks.
    Token can be stateless and can be shared among multiple services to
    reduce the pressure on the server, reduce frequent database queries, and make the server more robust .

  • Install vuex
    cnpm install vuex --save

2. Introduction to token usage

When the front and back ends are completely separated, the general idea of ​​implementing token verification in the Vue project is as follows:

1. When logging in for the first time, the front end adjusts the login interface of the back end and sends the user name and password

2. The backend receives the request, verifies the username and password, and if the verification succeeds, it returns a token to the frontend

3. The front end gets the token, stores the token in localStorage and vuex, and jumps to the routing page

4. Every time the front end jumps to the route, it judges whether there is a token in localStroage, jumps to the login page if there is no token, and jumps to the corresponding routing page

5. Every time the back-end interface is adjusted, token must be added to the request header

6. The backend judges whether there is a token in the request header. If there is a token, it will get the token and verify the token. If the verification succeeds, it will return the data. If the verification fails (for example, the token expires), it will return 401.

7. If the front end gets the status code of 401, it clears the token information and jumps to the login page
————————————————
Copyright statement: This article is the original work of the CSDN blogger "sleepwalker_1992" The article complies with the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this statement for reprinting.
Original link: https://blog.csdn.net/sleepwalker_1992/article/details/82974703


2. Create storage, store, request

1. src directory:

Insert picture description here
Note: Creating storage is optional, because I encapsulated localstorage (caching) into storage.js (the rest of this article uses js that I encapsulated);Create store is necessary

2. Create storage (optional)
// 封装操作localstorage本地存储的方法  模块化

var storage = {
    
    
    set(key, value) {
    
    
        localStorage.setItem(key, JSON.stringify(value));
        // localStorage.key = value;
        // localStorage[key] = value;
    },
    get(key) {
    
    
        return JSON.parse(localStorage.getItem(key));
    },
    getForIndex(index) {
    
    
        return localStorage.key(index);
    },
    getKeys(){
    
    
        let items = this.getAll();
        let keys = [];
        for (let index=0;index<items.length;index++){
    
    
            keys.push(items[index].key);
        }
        return keys;
    },
    getLength() {
    
    
        return localStorage.length;
    },
    getSupport() {
    
    
        return (typeof (Storage) !== "undefined") ? true : false;
    },
    remove(key) {
    
    
        localStorage.removeItem(key);
    },
    removeAll() {
    
    
        localStorage.clear();
    },
    getAll() {
    
    
        let len = localStorage.length;  // 获取长度
        let arr = new Array(); // 定义数据集
        for (var i = 0; i < len; i++) {
    
    
            // 获取key 索引从0开始
            var getKey = localStorage.key(i);
            // 获取key对应的值
            var getVal = localStorage.getItem(getKey);
            // 放进数组
            arr[i] = {
    
    
                'key': getKey,
                'val': getVal,
            }
        }
        return arr;
    }
}

export default storage;
3. Create a store

import Vue from 'vue'
import Vuex from 'vuex'
import storage from '@/model/storage'

Vue.use(Vuex);
// 用Vuex.Store对象用来记录token
const store = new Vuex.Store({
    
    

  state: {
    
    
    // 存储token
    // token: storage.get('token') ? storage.get('token') : '',
    token:"",
    userName:"" // 可选
  },

  actions: {
    
    
    // removeToken: () => {
    
    
      // context.commit('set_token')
    // }
  },

  // 计算属性
  mutations: {
    
    
    // 修改token,并将token存入localStorage
    set_token(state,token) {
    
    
      state.token = token;
      storage.set('token', token);
      console.log('store、localstorage保存token成功!');
    },
    del_token(state) {
    
    
      state.token = "";
      storage.remove("token");
    },
    // 可选
    setUserInfo(state, userName) {
    
    
      state.userName = userName;
    }
  }
});

export default store;
4. Create request
import axios from 'axios'
import store from '@/store'
import router from '@/router'

// create an axios instance
const service = axios.create({
    
    
    // index.js设置了代理(解决跨域) invoice = http://58.246.79.142:25005
    baseURL: "/invoice", // url = base url + request url
    timeout: 5000, // request timeout
   }

})

//添加请求拦截器,若token存在则在请求头中加token,不存在也继续请求
service.interceptors.request.use(
    config => {
    
    
        // 每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器,没有则不带token
        // Authorization是必须的
        if (store.state.token) {
    
    
            config.headers.Authorization = store.state.token;
        }
        return config;
    },
    error => {
    
    
        console.log("在request拦截器显示错误:", error.response)
        return Promise.reject(error);
    }
);

//respone拦截器
service.interceptors.response.use(
    response => {
    
    
        // 在status正确的情况下,code不正确则返回对应的错误信息(后台自定义为200是正确,并且将错误信息写在message),正确则返回响应
        return response.data.code == 200 ? response : Promise.reject(response.data.message);
    },
    error => {
    
     
        // 在status不正确的情况下,判别status状态码给出对应响应
        if (error.response) {
    
    
            console.log("在respone拦截器显示错误:", error.response)
            switch (error.response.status) {
    
    
                case 401:
                    //可能是token过期,清除它
                    // this.$store.commit("del_token"); 
                    store.commit("del_token");

                    router.replace({
    
     //跳转到登录页面
                        path: '/login',
                         // 将跳转的路由path作为参数,登录成功后跳转到该路由
                        query: {
    
     redirect: router.currentRoute.fullPath }
                    });
            }
        }
        return Promise.reject(error.response.data);
    }
);


export default service

3. Configure proxy, encapsulate router, set router guard, and import router and store into main.js

One, configure the proxy

Insert picture description here

2. Encapsulate routing router and set up routing guard
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/main/index'

import store from '@/store'
import storage from '@/model/storage'

Vue.use(Router)

const routes = [
  {
    
    
    path: '/',
    name: 'Main',
    redirect: '/login',
    // 某些页面规定必须登录后才能查看 ,可以在router中配置meta,将需要登录的requireAuth设为true,
    meta: {
    
    
      requireAuth: true,
    }
  },

  {
    
    
    path: '/login',
    component: () => import('@/views/login'),
  },

  {
    
    
    path: '/invoice',
    redirect: '/invoice',
    component: Main,
  },

    ]
  },
  {
    
    
    path: '*',
    component: Main
  }
]

const router = new Router({
    
     routes: routes })

// 设置路由守卫,在进页面之前,判断有token,才进入页面,否则返回登录页面
if (storage.get("token")) {
    
    
  store.commit("set_token", storage.get("token"));
}
router.beforeEach((to, from, next) => {
    
    
  // 判断要去的路由有没有requiresAuth
  // to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
  if (to.matched.some(r => r.meta.requireAuth)) {
    
    
    if (store.state.token) {
    
    
      next(); //有token,进行request请求,后台还会验证token
    } else {
    
    
      next({
    
    
        path: "/login",
        // 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
        query: {
    
     redirect: to.fullPath }  
      });
    }
  } else {
    
    
    next(); //如果无需token,那么随它去吧
  }
});

export default router
3. Introduce router and store in main.js
import Vue from 'vue'
import App from './App'
import router from './router'

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'
import store from '@/store'

// 兼容ie
// import 'babel-polyfill';

Vue.use(ElementUI);
Vue.config.productionTip = false
// 定义vue属性,就可以全局使用this.$store
Vue.prototype.$store = store;

new Vue({
    
    
  el: '#app',
  router:router,
  components: {
    
     App },
  template: '<App/>'
})

Fourth, the actual use of the login page

Only give the form submission example function and dependency js

import {
    
     postLogin } from "@/api/login";

submitForm(formName) {
    
    
      this.$refs[formName].validate(valid => {
    
    
        if (valid) {
    
    
          let that = this;
          // console.log('username',this.loginForm.username)
          // 通过校验规则后进入校验用户名密码是否正确
          // 没有后台接口可以自定义一个函数来模拟,替换postLogin
          postLogin(this.loginForm.username, this.loginForm.password)
            .then(res => {
    
    
              console.log(res);
              
               that.$store.commit("set_token", res.data.token);
               that.$store.commit("setUserInfo", res.data.account);

              this.$notify({
    
    
                title: "登录成功",
                type: "success",
                showClose: false,
                duration: 1000
              });
              setTimeout(() => {
    
    
                // 此时要判断/login后面的参数,若无参数,进入主页;
                this.$router.push("/index");
                // 若有参数则参数为未有权限的那个路由,跳转到那个路由
                // this.$router.push(***); -- 具体要自己在这实现
              }, 1000);
            })
            .catch(error => {
    
    
            // 错误分为 status-请求错误 和 code-账号密码错误
              this.$message.error(error);
              console.log(error);
            });
        } else {
    
    
        // 不符合前端校验
          this.$message.error('format error:'+error);
          console.log('format error:',error);
          return false;
        }
      });
    }

@/Api/login that the above depends on:

import request from '@/utils/request.js'  // 前面封装的request
// 在request中,设置了根路径/invoice,所以每一个请求都会自动在前面带上invoice;
// 由于在config中的index配置过了代理(监听)的路径也是/invoice,但凡遇到/invoice路径就会替换为target目标地址(后台接口根地址)
// 实现跨域,即不限于只能访问根路径为127.0.0.1下的地址
export function postLogin(account,password) {
    
    
    console.log(account,password)
    return request({
    
    
        url: '/login',
        method: 'post',
        params:{
    
    
        // 具体传参(键)要看后台要求
            account:account,
            password:password
        }
    })
}

Guess you like

Origin blog.csdn.net/GeniusXYT/article/details/104402757