前端鉴权+如何处理权限列表

前言

在写后台管理时,我们根据不同用户的权限,给用户展示不同的页面,后台定义用户的权限数据,前端进行获取,并渲染在侧边栏导航上,称为鉴权,这是页面级的鉴权,以及按钮级别的鉴权,那么如何编码呢?

思路:

login(登录,所有人均可见) 登录成功,获取权限 权限不同,侧边栏的数据展示不同

先定义一份公共的路由表,里面仅有一些公共的路由,如 login,404

获取到权限后,我们根据权限,得到需要动态添加的路由表,把这份路由表动态添加到router中即可。

github地址https://github.com/user-sunshilin/Vue-Demo
gitee地址:https://gitee.com/sunshilincsq/vue-demo

项目介绍,及知识点。

我这里的数据时使用自己模拟的json,结合elementUi去完成的,不要去看页面,知识点全在登录获取权限那边。

首先介绍一下登录

文件位置
在这里插入图片描述
json位置
在这里插入图片描述

代码介绍

 methods: {
      filterRouter(routers) {
        const accessedRouters = routers.filter(route => {
          if (route.component) {
              route.component = this._import(route.component)
          }
          if (route.children && route.children.length>0) {
              route.children = this.filterRouter(route.children)
          }
          return true
        })
        this.$store.commit("addUserRoute",accessedRouters)
        return accessedRouters
      },
      _import (file) {
        return () => import("@/views/" + file + ".vue")
      },
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            // 这里是登录成功了
            // 防抖
            clearTimeout(this.t);
            this.t = setTimeout(() => {
              // 多次触发只会执行一次
              login().then((res)=>{
                if(res.data.data.code==200)
                {
                  this.$message({
                    message: '登录成功',
                    type: 'success'
                  });
                  const token=res.data.data.token
                  // 使用封装的localStorage存储token
                  localStorage.setItem("token",JSON.stringify(token))
                  this.$router.replace("/")

                  // 登录成功获取用户权限列表

                  getUserRouteData().then((res)=>{
                    console.log(res.data.data.router)
                    this.filterRouter(res.data.data.router)
                  })
                }
                else{
                  this.$message({
                    message: '服务器异常,请重新登录',
                    type: 'warning'
                  });
                }
              })
            }, 300);
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      }
    }

filterRouter:这个函数是过滤json的,因为后台返回的数据时json形式的,这里我们结合_import这个函数给转换一下,明白人一眼看懂,看不懂私我教你啊!!!

submitForm:点击提交后触发,里面我使用的防抖进行了一下优化处理,
首先请求login接口,一会说一下封装的api,请求登录接口,获取到token,存下来,然后请求用户权限列表,获取到权限列表调用filterRouter这个过滤函数,然后把这些转换好的数据存入到vuex里面,最后登录成功跳转到首页。

登录完了吗??? 还么有 👉

main.js

router.beforeEach((to, from, next) => {
  // 开始进度条
  NProgress.start();
  
  // 如果去登录页面直接方形
  if(to.path==='/login')
  {
    return next()
  }
  // 如果没有token进入登录页面
  if(!localStorage.getItem("token"))
  {
    return next("/login")
  }
  else{
    // 如果有token判断是否有用户列表
    if (store.state.userRouteData.length === 0) { // 判断当前用户是否已拉取完user_info信息
      getUserRouteData().then((res)=>{
        filterRouter(res.data.data.router)
        router.options.routes = res.data.data.router
        router.addRoutes(res.data.data.router)
      })
    }
  }
  next()
});

function filterRouter(routers) {
  const accessedRouters = routers.filter(route => {
  if (route.component) {
      route.component = _import(route.component)
  }
  if (route.children && route.children.length>0) {
      route.children = filterRouter(route.children)
  }
  return true
})
store.commit("addUserRoute",accessedRouters)
return accessedRouters
}
function _import (file) {
  return () => import("@/views/" + file + ".vue")
}

router.afterEach(transition => {
  //结束进度条
  NProgress.done();
});

NProgress.configure({     
  easing: 'ease',  // 动画方式    
  speed: 500,  // 递增进度条的速度    
  showSpinner: false, // 是否显示加载ico    
  trickleSpeed: 200, // 自动递增间隔    
  minimum: 0.3, // 初始化时的最小百分比
  background:'red'
})

首先router.beforeEach时我们打开进度条,NProgress.start()这个是需要下载的
然后判断是否去login登录页面,如果是的话next放行,接下来判断是否有token,如果没有token,拦截让他跳转到登录页面,如果有token,判断用户列表存不存在,因为是使用的vuex吗,如果没用用户列表,重新去请求,转换,保存,和login页面的执行是一样的,最后router.afterEach中结束进度条,NProgress.configure是可以给他设置样式什么的,自行去看。

你以为这样login就处理完美了???,下面还有,最后一点👇

在http目录中的request.js文件中

在这里插入图片描述

// 请求拦截
service.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = token
  }
  return config
}, _error => {
  return Promise.reject('请求出错,请检查')
})

// 响应拦截
service.interceptors.response.use(res => {
  if(res.data.data.code == 10010 || res.data.data.code == 10011)
  {
    // 删除或者不删除token,反正后期都会给覆盖
    this.$router.replace("/login")
  }
  else if(res.data.data.token)
  {
    localStorage.setItem("token",res.data.data.token)
  }
  return res
}, error => {
  return Promise.reject('出错啦', error)
})

在请求拦截中拿到token,设置在header头上,每次请求都会携带,
在响应拦截中,判断这个返回的状态码这个返回的状态码是后台可以自定义的,你们商量就行,当请求接口是后端返回token过期状态码,直接替换到登录页面。

注意点

router

初始路由的时候我这里只写了一个login,注意 404页面必须要放在最后面,如果放在前面会导致所有在404页面后的路由访问不了
在这里插入图片描述

userRouteData.json

这个是我模拟的数据,要是写的话按照这种形式是比较方便的,应为我们要留一个home页面啊,用来渲染左侧以及右侧路由,或者一级一级的都可以,记住,你怎么方便你怎么来。

api

我这里的封装完全按照最简单来的,之前的博客有写,基本够用了axios封装

在这里插入图片描述
写到这里差不多完结了,最后就是左侧渲染,我这里使用的是elementui中的导航菜单

里面大概思路就是,首先判断里面是否有子菜单,有的话继续循环以此类推,没有的话直接渲染

<div>
      <el-menu
        :default-active="defaultActive"
        class="el-menu-vertical-demo"
        @open="handleOpen"
        @close="handleClose"
        background-color="#545c64"
        text-color="#fff"
        active-text-color="#ffd04b"
        :unique-opened="true"
        router
        :collapse="isCollapse"
      >
      <template>
        <i :class="[iconZkSq,'iconTem']" @click="zksq()"></i>
      </template>
      <template v-for="(e,i) in list">
        <template v-if="e.children">
          <el-submenu :index='i+""' :key="i">
            <template slot="title">
              <i :class="e.meta.icon"></i>
              <span slot="title">{{ e.meta.title }}</span>
            </template>
            <template v-for="subItem in e.children">
              <el-submenu v-if="subItem.children" :index="subItem.path" :key="subItem.path">
                <template slot="title">{{ subItem.meta.title }}</template>
                <el-menu-item 
                  v-for="(threeItem,i) in subItem.children"
                  :key="i"
                  :index="threeItem.path">
                    <i :class="threeItem.meta.icon"></i>
                    <span slot="title">{{ threeItem.meta.title }}</span>
                  <!-- {{ threeItem.meta.title }} -->
                </el-menu-item>               
              </el-submenu>
              <el-menu-item v-else :index="subItem.path" :key="subItem.path">
                <i :class="subItem.meta.icon"></i>
                <span slot="title">{{ subItem.meta.title }}</span>
                <!-- {{ subItem.meta.title }} -->
              </el-menu-item>
            </template>
          </el-submenu>  
        </template>
        <template v-else>
          <el-menu-item :index="e.path" :key="e.path">
            <i :class="e.meta.icon"></i>
            <span slot="title">{{ e.meta.title }}</span>
          </el-menu-item>
        </template>

      </template>

   </el-menu>
</div>
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值