前后端分离开发之权限篇,解决陷入死循环。

写这篇文章的目的是为了记录两天的成果。。
关于vue权限的参考是从花裤衩大佬那得到的启发

1、首先声明,阅读此博客需要:

  1. vuex 基础
  2. 了解mock
  3. 了解route/router
  4. 权限的实现
  5. 路由守卫

2、讲解一下本项目权限的实现:
权限:动态菜单栏,不同用户能看到的页面是不同的,所以,当用户登陆时,服务端判断该用户的权限,返回该用户的路由表–至于按钮级别的权限,可以用页面权限去实现,或者使用v-if命令实现【注意,前台不可以相信,权限还是要后台判断】
路由:vuex+localStorage实现【刷新重新拉取路由表】
3、实现思路+部分代码【后续代码全部贴出】
a.定义一个未登录可以查看的页面–路由守卫第一层判断

const whiteList = ['/login', '/solution/index',
 '/productIntroduction/index', 
'/seo/index', '/contactUs/index', '/farm/index', '/404'] 
// no redirect whitelist 没有重定向白名单
router.beforeEach((to, from, next) => {
  //如果含有token值,也就是处于登陆状态
  if (getToken()) {
  //不允许跳出登陆状态【本系统下规定】
    if (whiteList.indexOf(to.path) !== -1) {
      next({ path: '/' });
      NProgress.done();
    }else {
    .........................
    }

b.因为后台返回的数据是没有经过处理的,所以前台不能直接使用,还需进行一步转换
转换函数如下:递归

function filterAsyncRouter(asyncRouterMap) { //遍历后台传来的路由字符串,转换为组件对象
  const accessed = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'LayoutF') {//Layout组件特殊处理【本系统的布局组件】
        route.component = LayoutF
      } else {
        route.component = _import(route.component) //自定的引入组件的方式
      }
    }
    if (route.children && route.children.length) { //递归调用
      route.children = filterAsyncRouter(route.children)
    }
    return true
  });
  return accessed
}

c.在写本项目时,笔者遇到将经过处理的路由表放入localStorage中,用的时候再取,结果一直遇到陷入死循环的问题,且问题还未解决,大佬路过请指点~~【错误源码见本文尾】
笔者更改方法为每次用户刷新向服务器从新拉取路由表,这样一来,动态修改权限同时也就实现了。
定义一个变量为true,拉取完信息更改其为false,用户刷新则又变为true,这样,我们又多了一个判断条件

 //registerRouteFresh定义为true
  if (registerRouteFresh) {
  //判断vuex中是否含有异步路由表
        if(store.getters.asyncRouterMap.length ===0){
        //从服务器拉取路由表
          store.dispatch('RouteFresh',getToken()).then((data)=>{
          //返回空,则强制退出
            if(data===''){
              quit();
              next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
            }else{
            //如果vuex中含有异步路由表,将异步路由表转换
              GenerateRoutes(store.getters.asyncRouterMap);
              //将经处理路由放入内存中【定义全局变量,给菜单栏组件使用】
              global.antRouter = store.getters.addRouters;
              //将处理过的路由表放入localStorage中
              saveObjArr('routers', store.getters.addRouters);
              //添加路由
              router.addRoutes(store.getters.addRouters);
              //变量更改为false
              registerRouteFresh = false;
              next({...to, replace: true});
            }
          }).catch(err=>{
            console.log(err);
          });

d.核心的权限判断方法到这里就基本已经实现了。我的逻辑大致如此,至于源码,贴到下面~
1、permission.js:

var getRouter; //用来获取后台拿到的路由
let registerRouteFresh = true;
function GenerateRoutes(async) {
  let accessedRouters;
  accessedRouters = filterAsyncRouter(async);
console.log(accessedRouters)
  store.dispatch('AddRoutes', accessedRouters);
}

router.beforeEach((to, from, next) => {
  NProgress.start();
  if (getToken()) {

    /*
    * 更改方法判断值
    * */
    if (whiteList.indexOf(to.path) !== -1) {
      next({ path: '/' });
      NProgress.done();
    }else {
      if (registerRouteFresh) {
        if(store.getters.asyncRouterMap.length ===0){
          store.dispatch('RouteFresh',getToken()).then((data)=>{
            if(data===''){
              quit();
              next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
            }else{
              GenerateRoutes(store.getters.asyncRouterMap);
              //将路由放入内存中
              global.antRouter = store.getters.addRouters;
              saveObjArr('routers', store.getters.addRouters);
              router.addRoutes(store.getters.addRouters);
              registerRouteFresh = false;
              next({...to, replace: true});
            }
          }).catch(err=>{
            console.log(err);
          });
        }
        GenerateRoutes(store.getters.asyncRouterMap);
        //将路由放入内存中
        global.antRouter = store.getters.addRouters;
        saveObjArr('routers', store.getters.addRouters);
        router.addRoutes(store.getters.addRouters);
        registerRouteFresh = false;
        next({...to, replace: true});
      }else {
        next();
        // next({path:'',query:{redirect:to.path}});
      }
    }
  } else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
      // 如果当前页面为登录,则每次挂接后都不会触发,因此请手动处理它
    }
  }
});

router.afterEach(() => {
  NProgress.done() // finish progress bar
});

function saveObjArr(name, data) { //localStorage 存储数组对象的方法
  localStorage.setItem(name, JSON.stringify(data))
}

function getObjArr(name) { //localStorage 获取数组对象的方法
  return JSON.parse(window.localStorage.getItem(name));
}

//菜单栏生成函数
function filterAsyncRouter(asyncRouterMap) { //遍历后台传来的路由字符串,转换为组件对象
  const accessed = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'LayoutF') {//Layout组件特殊处理
        route.component = LayoutF
      } else {
        route.component = _import(route.component)
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    }
    return true
  });
  return accessed
}
function quit(){
  return new Promise(resolve => {
    removeToken();
    resolve(1);
  });
}

2、vuex中代码涉及隐私,这里就不贴出了。
3、前端mock接口数据

//静态访问数据
var arr = [];
var data = {
  "data": {
    "router": [
      {
        "path": "",
        "component": "LayoutF",
        "redirect": "dashboard",
        "children": [
          {
            "path": "dashboard",
            "component": "dashboard/index",
            "meta": {
              "title": "首页",
              "icon":"home"
            }
          }
        ]
      },
      {
        "path":"/Companys",
        "component": "LayoutF",
        "redirect": "/Companys/companystosave",
        "name":"Companys",
        "meta":{
          "title":"公司管理", "icon":"building"
        },
        "children":[
          {
            "path":"companystosave",
            "name":"companystosave",
            "component": "Jurisdiction/companyManagement/Addparentcompany",
            "meta":{"title":"添加母公司","icon":"plus"}
          },
          {
            "path":"findallCompany",
            "name":"findallCompany",
            "component": "Jurisdiction/companyManagement/Companylist",
            "meta":{"title":"公司列表","icon":"list"}
          },
          {
            "path":"managerform",
            "name":"managerform",
            "component": "Jurisdiction/companyManagement/RegisteredCompanyAdministrator",
            "meta":{"title":"注册公司管理员","icon":"registered"}
          },
          {
            "path":"managerlist",
             "name":"managerlist",
            "component":"Jurisdiction/companyManagement/Queryparentcompanyadministrator",
            "meta":{"title":"查询母公司管理员","icon":"search"}
          },
          {
            "path":"othermanagerlist",
            "name":"othermanagerlist",
            "component": "Jurisdiction/companyManagement/SearchSubsidiaryAdministrator",
            "meta":{"title":"查询子公司管理员","icon":"search"}
          },
          {
            "path":'findByCompanyName',
            "name":"findByCompanyName",
            "component": "Jurisdiction/companyManagement/Companyinformation",
            "meta":{"title":"公司信息","icon":"info"}
          },
          {
            "path":'addSonCompany',
            "name":"addSonCompany",
            "component": "Jurisdiction/companyManagement/Addparentcompany",
            "meta":{"title":"添加子公司","icon":"plus"}
          }
        ]
      },
      {
        "path": "*",
        "redirect": "/404",
        "hidden": true
      }
      ]
  }
};
for (let i in data.data.router) {
  arr.push(data.data.router[i]); //属性
  //arr.push(obj[i]); //值
}
export default {
  loginByUsername: config => {
    console.log(config);
    return  arr;
  },
  fresh:config=>{
    console.log(config);
    return arr;
  },
  logout: () => 'success'
}

f.上面贴出的代码为我测试开发的代码,未经过优化。下面贴出我的错误代码【这样写,刷新路由解析不出,并且陷入死循环】

/*权限管理*/
router.beforeEach((to, from, next) => {
  NProgress.start();
  if(getToken()){
    //vuex中含有token
    console.log(store.getters.token);
    console.log(getToken());
    if(getToken() === store.getters.token && store.getters.token !== null){
      console.log('vux中也含有token,并且相等');
      saveObjArr('token',store.getters.token);
      if(whiteList.indexOf(to.path) !== -1){
        next({ path: '/' });
      }else {
        console.log(store.getters.status);
        if(store.getters.status ==='0'){
          console.log(store.getters.asyncRouterMap);
          GenerateRoutes(store.getters.asyncRouterMap).then(()=>{
            console.log(store.getters.addRouters);
            //将路由放入内存中
            console.log(store.getters.permission_routers);
            global.antRouter = store.getters.addRouters;
            saveObjArr('store.getters.addRouters',store.getters.addRouters);
            router.addRoutes(store.getters.addRouters);
            store.dispatch('LoginSetStatus', 1);
            localStorage.setItem('status',store.getters.status);
            next({ ...to, replace: true });
             }).catch((err)=>{
               console.log(err);
              // store.dispatch('FedLogOut').then(() => {
              //    Message.error(err);
              //   console.log(err);
              //   next({ path: '/' });
              // })
            });
        }else {
          console.log('else 3 else');
          /*可进行其它操作*/
            next();
            //next({ path: '/404', replace: true, query: { noGoBack: true }});
        }
      }
    } else  {
      console.log('重定向');
      global.antRouter = getObjArr('store.getters.addRouters');
      router.addRoutes(global.antRouter);
      store.dispatch('Refresh',getObjArr('token'));
      store.dispatch('LoginSetStatus', 1);
      store.dispatch('AddRoutes', global.antRouter);
      next({ ...to, replace: true });
      // next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
    }
      }else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
      // 如果当前页面为登录,则每次挂接后都不会触发,因此请手动处理它
    }
  }
  router.afterEach(() => {
    NProgress.done() // finish progress bar
  })

});

感谢阅读此博客~错误的地方还请留言指正,共勉。。。

你可能感兴趣的:(vue,前后端分离)