vue项目结合vuex、localstorage实现本地储存token验证登陆注册

  • 首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间生成 token ,返回给前端;
  • 前端拿到后端返回的 token ,存储在 localStroage 和 Vuex 里;
  • 前端每次路由跳转,判断 localStroage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态;
  • 每次请求接口,在 Axios 请求头里携带 token;
  • 后端接口判断请求头有无 token,没有或者 token 过期,返回401;
  • 前端得到 401 状态码,重定向到登录页面。

store.js

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'

Vue.use(Vuex)
const vuexLocal = new VuexPersistence({
  storage: window.localStorage,
  reducer: state => ({
    token: state.token, // 这个就是存入localStorage的值
    WXTempKey: state.WXTempKey
  })
})
const store = new Vuex.Store({
  state: {
    token: "",
    WXTempKey: '',
    isLogin: false,
    uid: 0,
    name: '',
    avatar: ''
  },
  mutations: {
    setToken(state, token) {
      state.token = token
    },
    setLogin(state, flag) {
      state.isLogin = flag
    },
    setWXTempKey(state, WXTempKey) {
      state.WXTempKey = WXTempKey
    },
  },

  actions: {},
  plugins: [vuexLocal.plugin]
})

export default store

登陆页

    // 登陆方法
    async login() {
      if (!this.phone || !this.password) {
        // this.$toasted.error("请输入完善信息", { icon: "error" }).goAway(2000);
        Notify({ type: 'danger', message: "请输入完善信息", duration: 1500 })
        return;
      }
      
      try {
        // await等待一个异步返回的结果 如果没有await 会报user is undefined 获取不到
        let res = await this.http.post("/api/TokenAuth/MemberAuthenticate", {
          Phone: this.phone,
          PhoneCode: this.password,
          WXTempKey: this.$store.state.WXTempKey
            ? this.$store.state.WXTempKey
            : ""
        });
        console.log(res);
        if (res.Result) {
          // debugger
          // console.log(res);
          // localStorage.setItem("token","res.Result.AccessToken");
          this.$store.commit("setToken", res.Result.AccessToken);
          Toast({
            mask: false,
            message: '登录成功',
            position: 'bottom'
          });
          this.$router.replace({ name: "index" });
        } else {
          // this.$toasted
          //   .error(res.Error.Message, { icon: "error" })
          //   .goAway(2000);
        }
      } catch (error) {
        // this.$toasted.error(error.message, { icon: "error" }).goAway(2000);
      }
    }

http.js

import axios from 'axios'
import store from './store'
import { getToken } from '@/util/token'
import qs from 'qs'
import config from './config'
import router from './router'
import Vue from 'vue'
import VuexPersistence from 'vuex-persist'
import { Notify,Toast } from 'vant'

console.log(config)

// 创建axios实例
const http = axios.create({
  baseURL: config.api_base,
  timeout: 15000,
 //设置请求头
  headers: {
    // 'Content-Type': 'application/x-www-form-urlencoded'
    'Content-Type' : 'application/json',
  }
//利用qs解析json数据让后端可以解析

  transformRequest: [

  function(data, headers) {

  data = data || {}

  data = qs.stringify(data)

  return data

  }

  ]

})

// request拦截器
http.interceptors.request.use(config => {
  config.data = config.data ? config.data : {}
  config.headers["authorization"] =store.state.token; //把token添加到请求头每次请求接口时候带上
if(config.method=='get'){
    Toast({
      mask: false,
      message: '加载中...',
      position: 'bottom'
    });
  }else{
    Toast.loading({
      mask: true,
      message: '加载中...',
      position: 'middle',
      loadingType:'spinner'
    });
  }
  if (router.currentRoute && router.currentRoute.meta.requiresAuth) {
    if (localStorage.getItem('user')) {
      return Promise.reject('请登录后操作')
    } else {

      // config.headers.Authorization = token;

      // config.headers['authorization'] = `${store.state.token}`;
        console.log('interceptors config=',config)
      return config
    }
  } else {
    return config
  }
})

// respone拦截器
http.interceptors.response.use(
  response => {
    let data = response.data

    let msg = data.message || ''
    Toast.clear();
    if (response.status === 200) {
      if (response.data.Result) {
        if (response.data.Result.Code && response.data.Result.Code !== 0) {
          Notify({ type: 'danger', message: response.data.Result.Message, duration: 1500 })
          return Promise.resolve(response.data)
        } else {
          return Promise.resolve(response.data)
        }
      }else
      {
        return Promise.resolve(response.data)   
      }
    } else {
      Notify({ type: 'danger', message: '请求失败,请稍后再试!!!', duration: 1500 })
      return Promise.reject(response)
    }

    // if (data.code == 401) {
    //   // 账号禁用
    //   Vue.toasted.error(data.message, { icon: 'error' }).goAway(2000)
    //   router.replace({ name: 'login' })
    //   return Promise.reject(new Error(msg))
    // } else if (data.code == 403) {
    //   // 登录失效 需重新登录
    //   Vue.toasted.error('登录失效 需重新登录', { icon: 'error' }).goAway(2000)
    //   router.replace({ name: 'login' })
    //   return Promise.reject(new Error(msg))
    // } else if (data.code == 400) {
    //   return Promise.reject(new Error(msg))
    // }
    // return data
  },
  err => {
    Toast.clear();
    if (err.response.data.Error) {
      Notify({ type: 'danger', message: err.response.data.Error.Message, duration: 1500 })
      if(err.response.status == 401 || (err.response.data.Error && err.response.data.Error.Code)){
        console.log(router)
        store.commit("setToken", '');
        router.push({
            path: "/login",
            query:{
              redirect:router.currentRoute.fullPath
            }
        });
      }
    } else {
      Notify({ type: 'danger', message: '请求失败!!!', duration: 1500 })
    }
    return Promise.reject(err)
  }
)

export default http