vue前端登录权限配置

vue前端登录权限配置

这是家庭财务管理系统的前端页面,通过vue实现,实现的令牌登录与动态加载路由的功能
根据vue-admin-template进行修改

Build Setup

# 克隆项目
git clone https://github.com/chenyaohlgs/ffms-vue.git

# 进入项目目录
cd ffms-vue

# 安装依赖
npm install

# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org

# 启动服务
npm run dev

浏览器访问 http://localhost:9528

发布

# 构建测试环境
npm run build:stage

# 构建生产环境
npm run build:prod

其它

# 预览发布环境效果
npm run preview

# 预览发布环境效果 + 静态资源分析
npm run preview -- --report

# 代码格式检查
npm run lint

# 代码格式检查并自动修复
npm run lint -- --fix

令牌登录的实现

  • 具体的令牌登录实现的思想是通过这样的:前端在想后端发送登录请求的时,等到后端发送回来的token
    前端会根据token向后端发送getUserInfo请求获取用户信息,并将token存放在cookie中。此后每次前端发送请求时,都在请求头中添加token

  • 设置前端拦截器以及后端拦截器

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

// create an axios instance
const service = axios.create({
  // baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  baseURL: 'http://localhost:8080',
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 50000 // 请求超时时间
})

// request interceptor
service.interceptors.request.use(
  config => {
    // console.log('开始执行前置过滤器')
    if (store.getters.token) {
      // 在每次发送请求的时候都在请求头中携带token
      config.headers['token'] = getToken()
    }
    // console.log('结束执行前置过滤器')
    return config
  },
  error => {
    // console.log('前置控制出现失败')
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * 如果你想要获取http信息,可以使用下面的语句
   * return  response => response
  */

  /**
   * res.code是与后端进行规定的,也可以统一使用http的状态码
   */
  response => {
    // console.log('开始执行后置过滤器')
    const res = response.data
    // console.log(res)
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service // export default每个js文件只能出现一次,export可以出现多次

  • 事件触发函数login/index.vue
this.$refs.loginForm.validate(valid => { // 首先进行前端的校验
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/login', this.loginForm).then(() => { //执行vuex中的loign方法
            this.$router.push({ path: this.redirect || '/' }) // 登录成功后重定向到首页
            this.loading = false
          }).catch(() => {
            this.loading = false
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
  • this.$store.dispatch这个语句是执行store中user.js文件中的login方法
// user.js
login({ commit }, userInfo) {
    const username = userInfo.username.trim()
    return new Promise((resolve, reject) => {
      login(username, userInfo.password).then(response => {
        const data = response.data
        setToken(data.token)
        commit('SET_TOKEN', data.token) //SET_TOKEN就是将token保存在state中
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response
        // console.log(data)
        if (!data) {
          return reject('Verification failed, please Login again.')
        }
        const name = data.name
        const avatar = data.avatar
        const role = data.role
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_ROLE', role)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  }
  • 用户登录成功之后,我们会在全局钩子router.beforeEach中拦截路由,判断是否已获得token,在获得token之后我们就要去获取用户的基本信息了
router.beforeEach(async(to, from, next) => {
    const hasToken = getToken()
  // console.log(hasToken)
  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      // console.log(hasGetUserInfo)
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user info
          console.log(hasGetUserInfo)
          await store.dispatch('user/getInfo')
        }
        catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
      }
    }
  }
}
else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
}

权限处理

  • 权限处理在前后端都要完成,因为前端权限控制是不够安全的,但是前端的权限控制可以给用户很好的体验

  • 本文采用的前端控制思想主要是是通过用户信息中的角色属性进行路由的动态加载设置。通过关键的addRoutes代码进行动态加载路由

  • 首先配置路由表 src/router/index.js

// router.js
import Vue from 'vue';
import Router from 'vue-router';

import Login from '../views/login/';
const dashboard = resolve => require(['../views/dashboard/index'], resolve);
//使用了vue-routerd的[Lazy Loading Routes
](https://router.vuejs.org/en/advanced/lazy-loading.html)

//所有权限通用路由表 
//如首页和登录页和一些不用权限的公用页面
export const constantRouterMap = [
  { path: '/login', component: Login },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    name: '首页',
    children: [{ path: 'dashboard', component: dashboard }]
  },
]

//实例化vue的时候只挂载constantRouter
export default new Router({
  routes: constantRouterMap
});

//异步挂载的路由
//动态需要根据权限加载的路由表 
export const asyncRouterMap = [
  {
    path: '/permission',
    component: Layout,
    name: '权限测试',
    meta: { role: ['admin','super_editor'] }, //页面需要的权限
    children: [
    { 
      path: 'index',
      component: Permission,
      name: '权限测试页',
      meta: { role: ['admin','super_editor'] }  //页面需要的权限
    }]
  },
  { path: '*', redirect: '/404', hidden: true }
];
  • 关键的是src/permission.js,要同时修改store中的getter.js以及index.js文件
router.beforeEach(async(to, from, next) => {
    const hasToken = getToken()
  // console.log(hasToken)
  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      // console.log(hasGetUserInfo)
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user info
          console.log(hasGetUserInfo)
          await store.dispatch('user/getInfo').then(() => {
            const roles = store.getters.role
            console.log(roles)
            store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
              // console.log(store.getters.role)
              router.options.routes = store.getters.addRouters // this.router.options.routes 为了菜单组件获取
              router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
              next({ ...to, replace: true })
            })
          })
        }
        catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
      }
    }
  }
}
else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
}
  • store/modules/permission.js
import { asyncRouterMap, constantRouterMap } from '@/router'

function hasPermission(roles, route) {
  if (route.meta && route.meta.role) {
    return route.meta.role.indexOf(roles) > -1
  } else {
    return true
  }
}

const state = {
  routers: constantRouterMap,
  addRouters: []
}

const mutations = {
  SET_ROUTERS: (state, routers) => {
    state.addRouters = routers
    console.log(state.addRouters)
    state.routers = constantRouterMap.concat(routers)
  }
}
const actions = {
  GenerateRoutes({ commit }, data) { // data参数传入的是角色
    return new Promise(resolve => {
      const { roles } = data
      const accessedRouters = asyncRouterMap.filter(v => {
        if (roles.indexOf('admin') >= 0) {
          return true// 如果角色中有admin角色,则返回所有的挂载路由
        }
        if (hasPermission(roles, v)) {
          if (v.children && v.children.length > 0) {
            v.children = v.children.filter(child => {
              if (hasPermission(roles, child)) {
                return child
              }
              return false
            })
            return v
          } else {
            return v
          }
        }
        return false
      })
      commit('SET_ROUTERS', accessedRouters)
      console.log(accessedRouters)
      resolve()
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值