vue项目实战-----登录态管理(路由守卫+vuex+Axios)

目的:保存用户登录状态,刷新页面时做到用户状态不变:一刷新就去localStorage中获取token与islogin对vuex中的信息进行更新

1.路由拦截/守卫+token+vuex管理 

逻辑:

1.控制页面跳转

如果下一个页面需要登录,判断用户是否登录/是否有token:登录了且下一个不是登录界面就进入,是登录界面就去home;用户没有登录就去登录页------登录后得到token,将token存到store中以及本地。

如果下一个页面不需要登录,直接进入

2.刷新时保存状态

刷新时store会清零,这时就去缓存中拿token,然后去后台获取用户的信息,并将信息存储到store中

3.控制页面登录退出:登录与退出登录时更新状态中islogin

退出时要清空本地缓存哦

难点:

对于islogin,其值为true表示登录,false表示未登录,存放在localStorage里面,因为localStorage里面只能存字符串,所以存进去的时候需要localStorage.setItem(‘islogin’,JSON.stringify(islogin));将islogin变为String类型,取出来的时候需要将islogin转化为Boolean类型,就比如JSON.parse(localStorage.getItem(‘islogin’))这样。

或者可以直接将true/false保存为字符串进行存储

疑难解答

为什么本地存储了islogin或者token,还要在状态管理中进行管理呢?

islogin登录态的管理,vue不能实时监测localStorage的变化,需要实时监测islogin的变化来在页面显示登录还是已经登录,同时vuex中是只要刷新页面就会实时变化的,因此需要在状态管理中管理。

可以这样理解:本地化的存储只是为了控制刷新时的vuex更新,实际控制状态与检测的是vuex中的变量

最安全的就是在点击退出的时候清除本地缓存

islogin和token是否可以只需要一个,还是两个必须都有吗

严格意义上是需要两者都有的,主要是token;token从前后端的通信中进行传输携带,更加安全的验证

而且当状态为登录且用户信息也存在的时候才能算登录,因为token是一直存在的,登录状态不是。因此islogin为了判断用户是否登录,token可以进行前后端的校验

如何做到刷新页面时用户状态不变

页面刷新时,通过路由守卫判断,然后去本地存储中读取信息进行vuex的同步更新

流程

1.路由的配置path时;在需要守卫的path加上meta属性;用于表示下一个路由是否需要在登录后才能进入。

eg:

{path: '/home',component: home,meta:{requireAuth:true}}

2.全局守卫在main.js中设置        白名单中的直接访问,黑名单的需要登录

 router.beforeEach((to, from, next) => {

if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
    if(JSON.parse(localStorage.getItem('islogin'))){ //判断本地是否存在token
      if(to.path=='/login'){ 
        next({
              path:'/home'   //或者回到当前的路径,不进行跳转   from.path
             })}
      else{next();}
      
    }else {
      next({
        path:'/login'
      })
    }
  }
  else {
    next();
  }

}

路由独享守卫

为防止,不经过某路由就直接 在地址栏输入本路由地址而跳转至 本路由,导致本路由获取不到前面路由的某些数据,所以给本路由加 路由独享守卫。

在每一个路由中加而不是在整体的路由上加beforeEnter及逻辑处理

3.store.js    用于保存与更新当前的状态信息

/store.js中
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state:{
	//是否登录判断
	islogin:''
  },
  mutations:{
    login:(state,n) => {
	//传入登录状态islogin
	let islogin = JSON.parse(n);
	localStorage.setItem('islogin',JSON.stringify(islogin));
	console.log(islogin);
	state.islogin = islogin;
    }
  }
}

4.页面中触发登录态的地方     状态变更时更新vuex

//这里是登录
login() {
	let flag = true;
	this.$store.commit('login',flag);
	this.$router.push("/home");
	console.log("登录成功");
}
//这里是退出登录
exit() {
	let flag = false;
	this.$store.commit('login',flag);
	this.$router.push("/login");
	console.log("退出登录");
}

当前局限:只是限制了用户访问时路由页面的跳转

2.封装 Axios 请求

目的

  1. 每次请求时需要携带token到后台,进行身份认证;
  2. 项目需要根据响应回来的自定义状态码,决定是否跳转到登陆页面。

流程

1.axios

//axios 进行二次封装
//引入axios
import axios from "axios";
//在当前模块引入store
import store from '@/store';
//引入进度条 和 进度条样式  * 非必须
import nprogress from 'nprogress';
import 'nprogress/nprogress.css';
 
//1.利用axios对象的方法create,去创建一个axios实例
//2.这里requests就是axios,只不过稍微配置一下
const requests = axios.create({
     //配置对象
     //基础路径,发请求的时候,路径中会出现api
     baseURL:"/api",
     //请求超时的时间为5s
     timeout:5000,
 });
// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
requests.interceptors.request.use((config)=>{
    if(store.state.detail.uuid_token) {
        config.headers.userTempId = store.state.detail.uuid_token;
    }
    //需要携带token带给服务器
    if(store.state.user.token) {
        config.headers.token = store.state.user.token;
    }
    //进度条开始
    nprogress.start();
    return config;
});
//相应拦截器
requests.interceptors.response.use((res)=>{
    //成功回调
    //进度条结束
    nprogress.done();
    return res.data;
},(error)=>{
    //失败回调
    new Promise.reject(new Error('faile'));
    
});
 
 
 
 
export default requests;

2.vuex     store.js

import {reqGetCode,reqUserRegister,reqUserLogin,reqUserInfo,reqLogout} from '@/api';
import {setToken,getToken,removeToken} from '@/utils/token'
 
//home模块“小”仓库
 
//state:数据仓库
const state = {
    code:'',
    token:getToken(),
    userInfo:{}
}
 
//actions:可以处理自己的业务逻辑,也可以异步
const actions = {
    //获取验证码
    async getCode({commit},phone) {
       let result = await reqGetCode(phone);
    //    console.log(result)
       if(result.code == 200 ){
           commit("GETCODE",result.data);
           return "ok";
       } else {
           return Promise.reject(new Error('faile'));
       }
    },
    //用户注册
    async userRegister({commit},user){
        let result = await reqUserRegister(user);
        if(result.code == 200 ) {
            return 'ok';
        } else {
            return Promise.reject(new Error(result.message));
        }
 
    },
    //登陆业务
    async userLogin({commit},data) {
        let result = await reqUserLogin(data);
        if(result.code == 200) { 
            commit('USERLOGIN',result.data.token);
            //持久化存储token
            setToken(result.data.token);
            return 'ok';
        } else {
            return Promise.reject(new Error(result.message));
        }
    },
    //获取用户信息
    async getUserInfo({commit}) {
        let result = await reqUserInfo();
        if(result.code == 200) { 
            commit('GETUSERINFO',result.data);
            return 'ok';
        } else {
            return Promise.reject(new Error(result.message));
        }
    },
    //退出登录
    async userLogout({commit}) {
        let result = await reqLogout();
        if(result.code == 200) {
            commit('CLEAR');
            return 'ok';
        } else {
            return Promise.reject(new Error(result.message));
        }
    }
}
 
const mutations = {
    GETCODE(state,code) {
        state.code = code;
    },
    USERLOGIN(state,token) {
        state.token = token;
    },
    GETUSERINFO(state,userInfo) {
        state.userInfo = userInfo;
    },
    //清楚本地用户数据
    CLEAR(state) {
        state.token = '';
        state.userInfo = {};
        removeToken();
    }
    
}
 
 
export default {
    state,
    mutations,
    actions,
    getters,
}

3.token管理

//存token
export const setToken = (token) => {
    localStorage.setItem('TOKEN',token);
}
//取token
export const getToken = () => {
    return localStorage.getItem('TOKEN');
}
//清除token
export const removeToken = () => {
    localStorage.removeItem('TOKEN');
}
 

4.路由配置界面   或者是main.js

//路由配置文件
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './routes';
//使用VueRouter插件
Vue.use(VueRouter);
//引入store 
import store from '@/store';
 
 
//先保存一份VueRouter
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写push|replace
//第一个参数:往哪里跳(传递哪些参数)
/** 
 * call 和 apply 区别
 *  相同点:都可以调用函数一次,都可以篡改函数的上下文一次
 *  不同点:call与app传递参数用逗号隔开,apply方法执行,传递数组
 */
VueRouter.prototype.push = function(location,resolve,reject) {
    if(resolve && reject) {
        originPush.call(this,location,resolve,reject);
    } else {
        originPush.call(this,location,()=>{},()=>{});
    }
}
VueRouter.prototype.replace = function(location,resolve,reject) {
    if(resolve && reject) {
        originReplace.call(this,location,resolve,reject);
    } else {
        originReplace.call(this,location,()=>{},()=>{});
    }
}
 
//配置路由
 let router = new VueRouter({
    //路由配置
    routes,
    scrollBehavior (to,from,savedPosition) {
        return {y:0}
    }
});
 
//理由全局守卫,前置守卫(跳转之前判断)
router.beforeEach(async(to,from,next) => {
    // next();
    //获取token
    let token = store.state.user.token;
    let name = store.state.user.userInfo.name;
    if(token) {
        //用户已经登录
        if(to.path == '/login') {
            next('/home');
        }else{
            if(name){
                next(); 
            } else {
                try{
                    await store.dispatch('getUserInfo');
                    next();
                } catch(error) {
                    //清楚token
                    await store.dispatch('userLogout');
                    next('/login');
                }
            }
        }
    } else {
        // 用户未登录
        let toPath = to.path;
        if(toPath.indexOf('/trade') != -1 || 
            toPath.indexOf('/pay') != -1 || 
            toPath.indexOf('/pausuccess') != -1 || 
            toPath.indexOf('/center') != -1||
            toPath.indexOf('/Search') != -1||
            toPath.indexOf('/shopcart') != -1
            ) {
            next('/login');
        } else {
            next();
        }
    }
});
 
export default router;

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值