vue项目将token存在(vuex)store和localstorage中

28 篇文章 1 订阅


适合小白 ❤

一、准备工作和token

1、准备工作
  • 了解(session,cookie)token
    Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
    token 是在服务端产生的一串字符串,以作客户端进行请求的一个令牌。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌(除非设置了有效期)。

  • token 优点
    Token 完全由应用管理,所以它可以避开同源策略
    Token 可以避免 CSRF 攻击
    Token 可以是无状态的,可以在多个服务间共享
    减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。

  • 安装vuex
    cnpm install vuex --save

2、介绍token用法

在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:

1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码

2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token

3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面

4、前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面

5、每次调后端接口,都要在请求头中加token

6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401

7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
————————————————
版权声明:本文为CSDN博主「sleepwalker_1992」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sleepwalker_1992/article/details/82974703


二、创建storage,store,request

1、src目录:

在这里插入图片描述
注:

  • 封装storage是可选的,不封装就用原生的呗;创建store是必须的
  • token一定要存在storage缓存中,否则刷新一下,store会重新被加载,token就没了;
  • 那存在store是不是多余了,这个也是为了数据统一管理吧,也是数据可视化,因为缓存中的数据代码中是看不见的~
2、封装storage(可选)
// 封装操作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、创建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:"",
    userName:"" // 可选
  },

  getters: {
	getToken(state){
		return state.token || storage.get("token") || "";
	}
  },

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

 actions: {
   // removeToken: (context) => {
     // context.commit('setToken')
   // }
 },
});
  
export default store;
4、创建request
import axios from 'axios'
import store from '@/store'
import router from '@/router'

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

//添加请求拦截器,若token存在则在请求头中加token,不存在也继续请求
service.interceptors.request.use(
    config => {
        // 每次发送请求之前都检测vuex是否存有token,放在请求头发送给服务器
        // Authorization是根据后端自定义字段
        config.headers.Authorization = store.getters.getToken;
        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过期,清除它
                    store.commit("delToken");

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


export default service

三、配置代理,封装路由router、设置路由守卫,在main.js中引入router、store

一、配置代理

在这里插入图片描述

二、封装路由router,并设置路由守卫
/* 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("setToken", 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
三、在main.js中引入router、store
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

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

四、登录页面实际使用

只给出表单提交示例函数和依赖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("setToken", res.data.token);
               that.$store.commit("setUserInfo", res.data.account);

              this.$notify({
                title: "登录成功",
                type: "success",
                showClose: false,
                duration: 1000
              });
              setTimeout(() => {
                // 此时要判断/login后面的参数redirect,若无参数,进入主页;
                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:

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
        }
    })
}
Vue2.0 使用 Vuex 实现登录,可以将登录状态和用户信息存储在 Vuexstore ,以便在应用的任何地方都可以访问和使用。 下面是一个简单的示例代码: 1. 创建 store.js 文件,定义 store 的 state、mutations 和 actions: ```javascript import Vue from 'vue' import Vuex from 'vuex' import axios from 'axios' Vue.use(Vuex) export default new Vuex.Store({ state: { isLoggedIn: false, user: {} }, mutations: { login (state, user) { state.isLoggedIn = true state.user = user }, logout (state) { state.isLoggedIn = false state.user = {} } }, actions: { login ({ commit }, { username, password }) { return axios.post('/api/login', { username, password }) .then(response => { const user = response.data.user localStorage.setItem('token', response.data.token) commit('login', user) return user }) }, logout ({ commit }) { localStorage.removeItem('token') commit('logout') } } }) ``` 2. 在登录页面,使用 Vuex 的 mapActions 辅助函数将 login 方法映射到组件的 methods : ```html <template> <div> <input type="text" v-model="username" placeholder="请输入用户名"/> <input type="password" v-model="password" placeholder="请输入密码"/> <button @click="handleLogin">登录</button> </div> </template> <script> import { mapActions } from 'vuex' export default { data () { return { username: '', password: '' } }, methods: { ...mapActions(['login']), handleLogin () { this.login({ username: this.username, password: this.password }) .then(user => { // 登录成功,跳转到首页或其他页面 this.$router.push('/') }) .catch(error => { // 登录失败,提示错误信息 alert(error.response.data.message) }) } } } </script> ``` 3. 在应用的任何地方,都可以通过 Vuex 的 mapState 辅助函数或 $store.state 来访问登录状态和用户信息: ```html <template> <div> <p v-if="isLoggedIn">欢迎,{{ user.name }}!</p> <button v-if="isLoggedIn" @click="handleLogout">退出登录</button> </div> </template> <script> import { mapState, mapActions } from 'vuex' export default { computed: { ...mapState(['isLoggedIn', 'user']) }, methods: { ...mapActions(['logout']), handleLogout () { this.logout() .then(() => { // 退出登录成功,跳转到登录页面 this.$router.push('/login') }) } } } </script> ```
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值