精华内容
下载资源
问答
  • 后端控制路由

    2020-09-03 10:12:12
    后端控制路由 本篇文章是基于vue-element-admin的权限路由基础之上来做这个后端控制路由,也是因为项目需求故做的这个,话不多说,直接上代码 1.要想实现完全做到由后端控制路由 菜单显示,后台的小伙伴需要提供的接口有...

    后端控制路由

    本篇文章是基于vue-element-admin的权限路由基础之上来做这个后端控制路由,也是因为项目需求故做的这个,话不多说,直接上代码

    1.要想实现完全做到由后端控制路由 菜单显示,后台的小伙伴需要提供的接口有

    获取当前登录用户的信息接口
    获取当前用户所拥有权限菜单
    备注:当然这两个接口可以写成一个,这样也可以的,直接将用户所拥有的菜单直接返回到获取用户信息的接口里面(当然我们后端是直接把这些数据写成了一个接口的,哈哈)

    接口中需要包含的数据如下

    path
    name/title
    icon
    id
    parentId
    component
    

    2.代码:找到store文件夹下modules文件夹下的user.js,修改代码如下

    const getDefaultState = () => {
      return {
        token: getToken(),
        name: '',
        avatar: '',
        roles: [],
        onePermissionList: [] 
      }
    }
    
    const state = getDefaultState()
    
    const mutations = {
      RESET_STATE: (state) => {
        Object.assign(state, getDefaultState())
      },
      SET_TOKEN: (state, token) => {
        state.token = token
      },
      SET_NAME: (state, name) => {
        state.name = name
      },
      SET_AVATAR: (state, avatar) => {
        state.avatar = avatar
      },
      SET_ROLES: (state, roles) => {
        state.roles = roles
      },
      SET_onePermissionList: (state, onePermissionList) => {
        state.onePermissionList = onePermissionList
      }
    }
    
    const actions = {
      // user login
      login ({ commit }, userInfo) {
        const { username, password } = userInfo
        return new Promise((resolve, reject) => {
          login({ username: username.trim(), password: password }).then(response => {
            const { data } = response
            commit('SET_TOKEN', data.token)
            setToken(data.token)
            resolve()
          }).catch(error => {
            reject(error)
          })
        })
      },
    
      // get user info
      getInfo ({ commit, state }) {
        return new Promise((resolve, reject) => {
          getInfo().then(response => {
            const { data } = response
            commit('SET_NAME', nickname)
            commit('SET_AVATAR', avatar)
            commit('SET_onePermissionList', permissions) //在这里拿到后台返回的权限列表
            resolve(data)
          }).catch(error => {
            reject(error)
          })
        })
      }
    }
    
    export default {
      namespaced: true,
      state,
      mutations,
      actions
    }
    

    3.代码:找到与根目录同级的permission.js,修改代码如下

    router.beforeEach(async (to, from, next) => {
      NProgress.start()
      document.title = getPageTitle(to.meta.title)
    
      const hasToken = getToken()
      if (hasToken) {
        if (to.path === '/login') {
          next({ path: '/' })
          NProgress.done() 
        } else {
          const hasGetUserInfo = store.getters.name
          // console.log(store.getters.name)
          if (hasGetUserInfo) {
            next()
          } else {
            try {
              await store.dispatch('user/getInfo')  //获取用户信息
              const onePermissionList = store.getters.onePermissionList  //拿到后台给到的权限菜单
              const accessRoutes = await store.dispatch('permission/generateRoutes', onePermissionList)
              // 动态添加可访问路由表
              router.addRoutes(accessRoutes)
              next({ ...to, replace: true })
            } catch (error) {
              await store.dispatch('user/resetToken')
              Message.error(error || 'Has Error')
              next(`/login?redirect=${to.path}`)
              NProgress.done()
            }
          }
        }
      } else {
        /* has no token*/
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        } else {
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    })
    

    4.代码:找到store文件夹下modules文件夹下的permission.js,修改代码如下:

    export function arrayToMenu (array) {
      // console.log('array++++', array)
      const nodes = []
      // 获取父级菜单
      for (let i = 0; i < array.length; i++) {
        const row = array[i]
        // exists方法用来判断有没有二级菜单
        if (!exists(array, row.parentId)) {
          nodes.push({
            path: row.path,
            component: Layout, 
            name: row.name, 
            alwaysShow: true,
            meta: { title: row.name, icon: row.icon }, 
            id: row.id 
          })
        }
      }
      const toDo = Array.from(nodes)
      while (toDo.length) {
        const node = toDo.shift()
        // 获取二级菜单
        for (let i = 0; i < array.length; i++) {
          const row = array[i]
          // 判断一下二级菜单应该在哪个一级菜单下
          if (row.parentId === node.id) {
            const child = {
              path: row.path,
              name: row.name,
              // component判断
              component: require('@/views/' + row.component),
              meta: { title: row.name, icon: row.icon},
              id: row.id
            }
            if (node.children) {
              node.children.push(child)
            } else {
              node.children = [child]
            }
            toDo.push(child)
          }
        }
      }
      console.log(nodes)
      return nodes
    }
    // 判断一下是否有二级菜单
    function exists (rows, parentId) {
      for (let i = 0; i < rows.length; i++) {
        if (rows[i].id === parentId) return true
      }
      return false
    }
    
    const state = {
      routes: [],
      addRoutes: []
    }
    
    const mutations = {
      SET_ROUTES: (state, routes) => {
        state.addRoutes = routes
        state.routes = constantRoutes.concat(routes)
      }
    }
    
    const actions = {
      generateRoutes ({ commit }, roles) {
        // console.log('roles++++', roles)
        return new Promise(resolve => {
          const accessedRoutes = arrayToMenu(roles)
          accessedRoutes.concat([{ path: '*', redirect: '/404', hidden: true }])
          commit('SET_ROUTES', accessedRoutes)
          resolve(accessedRoutes)
        })
      }
    }
    
    export default {
      namespaced: true,
      state,
      mutations,
      actions
    }
    

    总结

    以上就是我来做这个后端控制路由的具体代码实现。代码还有很多值得优化的地方,欢迎各位大佬批评指正。

    展开全文
  • Vue动态路由使用(后端控制

    千次阅读 2020-07-18 18:20:10
    使用VUE开发后台管理系统 完全由后端控制左边菜单项思路 在传统开发后台管理系统时,都会涉及权限控制这一功能需求 即:根据不同登录的角色账号来使用该账号拥有的功能,也就是说系统左边的菜单栏不是固定不变的,...

    使用VUE开发后台管理系统 完全由后端控制左边菜单项思路

    在传统开发后台管理系统时,都会涉及权限控制这一功能需求 即:根据不同登录的角色账号来使用该账号拥有的功能,也就是说系统左边的菜单栏不是固定不变的,而是根据登录账号的权限去动态控制的,现在主流的两种模式
    即:1.前后端配合控制 2.完全由后端来控制

    本章着重来介绍 第二种模式 :由后端数据控制前端的菜单

    1. 借助Ant Design Pro Vue 来讲解实现思路 (Ant-Design-Pro Vue 开箱即用的企业后台管理系统)
    2. 码云地址 :https://gitee.com/sendya/ant-design-pro-vue.git

    一.实现思路

    1. 后端提供路由接口 前端请求数据然后二次处理

    2. 前端请求到的数据在这里插入图片描述

    3. 前端处理数据的逻辑

    // eslint-disable-next-line
    import * as loginService from '@/api/login'
    // eslint-disable-next-line
    import { BasicLayout, BlankLayout, PageView, RouteView } from '@/layouts'
    
    // 前端路由表  
    const constantRouterComponents = {
      // 基础页面 layout 必须引入
      BasicLayout: BasicLayout,
      BlankLayout: BlankLayout,
      RouteView: RouteView,
      PageView: PageView,
      '403': () => import(/* webpackChunkName: "error" */ '@/views/exception/403'),
      '404': () => import(/* webpackChunkName: "error" */ '@/views/exception/404'),
      '500': () => import(/* webpackChunkName: "error" */ '@/views/exception/500'),
    
      // 你需要动态引入的页面组件
      'Workplace': () => import('@/views/dashboard/Workplace'),
      'Analysis': () => import('@/views/dashboard/Analysis'),
    
      // form
      'BasicForm': () => import('@/views/form/basicForm'),
      'StepForm': () => import('@/views/form/stepForm/StepForm'),
      'AdvanceForm': () => import('@/views/form/advancedForm/AdvancedForm'),
    
      // // list
      'TableList': () => import('@/views/list/TableList'),
      // 'StandardList': () => import('@/views/list/StandardList'),
      'CardList': () => import('@/views/list/CardList'),
      'SearchLayout': () => import('@/views/list/search/SearchLayout'),
      'SearchArticles': () => import('@/views/list/search/Article'),
      'SearchProjects': () => import('@/views/list/search/Projects'),
      'SearchApplications': () => import('@/views/list/search/Applications'),
      'ProfileBasic': () => import('@/views/profile/basic'),
      'ProfileAdvanced': () => import('@/views/profile/advanced/Advanced'),
    
      // result
      'ResultSuccess': () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
      'ResultFail': () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
    
      // exception
      'Exception403': () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
      'Exception404': () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
      'Exception500': () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
    
      // account
      'AccountCenter': () => import('@/views/account/center'),
      'AccountSettings': () => import('@/views/account/settings/Index'),
      'BaseSettings': () => import('@/views/account/settings/BaseSetting'),
      'SecuritySettings': () => import('@/views/account/settings/Security'),
      'CustomSettings': () => import('@/views/account/settings/Custom'),
      'BindingSettings': () => import('@/views/account/settings/Binding'),
      'NotificationSettings': () => import('@/views/account/settings/Notification'),
    
      'TestWork': () => import(/* webpackChunkName: "TestWork" */ '@/views/dashboard/TestWork')
    }
    
    // 前端未找到页面路由(固定不用改)
    const notFoundRouter = {
      path: '*', redirect: '/404', hidden: true
    }
    
    // 根级菜单
    const rootRouter = {
      key: '',
      name: 'index',
      path: '',
      component: 'BasicLayout',
      redirect: '/dashboard',
      meta: {
        title: '首页'
      },
      children: []
    }
    
    /**
     * 动态生成菜单
     * @param token
     * @returns {Promise<Router>}
     */
    export const generatorDynamicRouter = (token) => {
      return new Promise((resolve, reject) => {
        loginService.getCurrentUserNav(token).then(res => {
          console.log('res', res) //后端请求到的数据
          const { result } = res
          const menuNav = []
          const childrenNav = []
          //      后端数据, 根级树数组,  根级 PID
          listToTree(result, childrenNav, 0) //路由数据 第一次处理
          rootRouter.children = childrenNav  
          menuNav.push(rootRouter)
          console.log('menuNav', menuNav)
          const routers = generator(menuNav) //路由数据 第二次处理
          routers.push(notFoundRouter)
          console.log('routers', routers)
          resolve(routers)
        }).catch(err => {
          reject(err)
        })
      })
    }
    
    /**
     * 格式化树形结构数据 生成 vue-router 层级路由表
     *
     * @param routerMap
     * @param parent
     * @returns {*}
     */
     //路由数据第二次处理  匹配加载组件 生成路由path 
    export const generator = (routerMap, parent) => {   
      return routerMap.map(item => {
        const { title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {}
        const currentRouter = {
          // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
          path: item.path || `${parent && parent.path || ''}/${item.key}`,
          // 路由名称,建议唯一
          name: item.name || item.key || '',
          // 该路由对应页面的 组件 :方案1
          // component: constantRouterComponents[item.component || item.key],
          // 该路由对应页面的 组件 :方案2 (动态加载)
          component: (constantRouterComponents[item.component || item.key]) || (() => import(`@/views/${item.component}`)),
          // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
          meta: {
            title: title,
            icon: icon || undefined,
            hiddenHeaderContent: hiddenHeaderContent,
            target: target,
            permission: item.name
          }
        }
        // 是否设置了隐藏菜单
        if (show === false) {
          currentRouter.hidden = true
        }
        // 是否设置了隐藏子菜单
        if (hideChildren) {
          currentRouter.hideChildrenInMenu = true
        }
        // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
        if (!currentRouter.path.startsWith('http')) {
          currentRouter.path = currentRouter.path.replace('//', '/')
        }
        // 重定向
        item.redirect && (currentRouter.redirect = item.redirect)
        // 是否有子菜单,并递归处理
        if (item.children && item.children.length > 0) {
          // Recursion
          currentRouter.children = generator(item.children, currentRouter)
        }
        return currentRouter
      })
    }
    
    /**
     * 数组转树形结构
     * @param list 源数组
     * @param tree 树
     * @param parentId 父ID
     */
     // 路由数据第一次 处理 将list结构转化为 tree
    const listToTree = (list, tree, parentId) => {
      list.forEach(item => {
        // 判断是否为父级菜单
        if (item.parentId === parentId) {
          const child = {
            ...item,
            key: item.key || item.name,
            children: []
          }
        
          // 迭代 list, 找到当前菜单相符合的所有子菜单
          listToTree(list, child.children, item.id)
          // 删掉不存在 children 值的属性
          if (child.children.length <= 0) {
            delete child.children
          }
          // 加入到树中
          tree.push(child)
        }
      })
    }
    

    最终生成的 路由数据
    在这里插入图片描述

    {
        path: '/',
        name: 'index',
        component: BasicLayout,
        meta: { title: 'menu.home' },
        redirect: '/dashboard/workplace',
        children: [
          // dashboard
          {
            path: '/dashboard',
            name: 'dashboard',
            redirect: '/dashboard/workplace',
            component: RouteView,
            meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: [ 'dashboard' ] },
            children: [
              {
                path: '/dashboard/analysis/:pageNo([1-9]\\d*)?',
                name: 'Analysis',
                component: () => import('@/views/dashboard/Analysis'),
                meta: { title: 'menu.dashboard.analysis', keepAlive: false, permission: [ 'dashboard' ] }
              },
              // 外部链接
              {
                path: 'https://www.baidu.com/',
                name: 'Monitor',
                meta: { title: 'menu.dashboard.monitor', target: '_blank' }
              },
              {
                path: '/dashboard/workplace',
                name: 'Workplace',
                component: () => import('@/views/dashboard/Workplace'),
                meta: { title: 'menu.dashboard.workplace', keepAlive: true, permission: [ 'dashboard' ] }
              }
            ]
          },
    
          // forms
          {
            path: '/form',
            redirect: '/form/base-form',
            component: RouteView,
            meta: { title: '表单页', icon: 'form', permission: [ 'form' ] },
            children: [
              {
                path: '/form/base-form',
                name: 'BaseForm',
                component: () => import('@/views/form/basicForm'),
                meta: { title: '基础表单', keepAlive: true, permission: [ 'form' ] }
              },
              {
                path: '/form/step-form',
                name: 'StepForm',
                component: () => import('@/views/form/stepForm/StepForm'),
                meta: { title: '分步表单', keepAlive: true, permission: [ 'form' ] }
              },
              {
                path: '/form/advanced-form',
                name: 'AdvanceForm',
                component: () => import('@/views/form/advancedForm/AdvancedForm'),
                meta: { title: '高级表单', keepAlive: true, permission: [ 'form' ] }
              }
            ]
          },
    
          // list
          {
            path: '/list',
            name: 'list',
            component: RouteView,
            redirect: '/list/table-list',
            meta: { title: '列表页', icon: 'table', permission: [ 'table' ] },
            children: [
              {
                path: '/list/table-list/:pageNo([1-9]\\d*)?',
                name: 'TableListWrapper',
                hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
                component: () => import('@/views/list/TableList'),
                meta: { title: '查询表格', keepAlive: true, permission: [ 'table' ] }
              },
              {
                path: '/list/basic-list',
                name: 'BasicList',
                component: () => import('@/views/list/BasicList'),
                meta: { title: '标准列表', keepAlive: true, permission: [ 'table' ] }
              },
              {
                path: '/list/card',
                name: 'CardList',
                component: () => import('@/views/list/CardList'),
                meta: { title: '卡片列表', keepAlive: true, permission: [ 'table' ] }
              },
              {
                path: '/list/search',
                name: 'SearchList',
                component: () => import('@/views/list/search/SearchLayout'),
                redirect: '/list/search/article',
                meta: { title: '搜索列表', keepAlive: true, permission: [ 'table' ] },
                children: [
                  {
                    path: '/list/search/article',
                    name: 'SearchArticles',
                    component: () => import('../views/list/search/Article'),
                    meta: { title: '搜索列表(文章)', permission: [ 'table' ] }
                  },
                  {
                    path: '/list/search/project',
                    name: 'SearchProjects',
                    component: () => import('../views/list/search/Projects'),
                    meta: { title: '搜索列表(项目)', permission: [ 'table' ] }
                  },
                  {
                    path: '/list/search/application',
                    name: 'SearchApplications',
                    component: () => import('../views/list/search/Applications'),
                    meta: { title: '搜索列表(应用)', permission: [ 'table' ] }
                  }
                ]
              }
            ]
          },
    
          // profile
          {
            path: '/profile',
            name: 'profile',
            component: RouteView,
            redirect: '/profile/basic',
            meta: { title: '详情页', icon: 'profile', permission: [ 'profile' ] },
            children: [
              {
                path: '/profile/basic',
                name: 'ProfileBasic',
                component: () => import('@/views/profile/basic'),
                meta: { title: '基础详情页', permission: [ 'profile' ] }
              },
              {
                path: '/profile/advanced',
                name: 'ProfileAdvanced',
                component: () => import('@/views/profile/advanced/Advanced'),
                meta: { title: '高级详情页', permission: [ 'profile' ] }
              }
            ]
          },
    
          // result
          {
            path: '/result',
            name: 'result',
            component: RouteView,
            redirect: '/result/success',
            meta: { title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },
            children: [
              {
                path: '/result/success',
                name: 'ResultSuccess',
                component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
                meta: { title: '成功', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }
              },
              {
                path: '/result/fail',
                name: 'ResultFail',
                component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
                meta: { title: '失败', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }
              }
            ]
          },
    
          // Exception
          {
            path: '/exception',
            name: 'exception',
            component: RouteView,
            redirect: '/exception/403',
            meta: { title: '异常页', icon: 'warning', permission: [ 'exception' ] },
            children: [
              {
                path: '/exception/403',
                name: 'Exception403',
                component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
                meta: { title: '403', permission: [ 'exception' ] }
              },
              {
                path: '/exception/404',
                name: 'Exception404',
                component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
                meta: { title: '404', permission: [ 'exception' ] }
              },
              {
                path: '/exception/500',
                name: 'Exception500',
                component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
                meta: { title: '500', permission: [ 'exception' ] }
              }
            ]
          },
    
          // account
          {
            path: '/account',
            component: RouteView,
            redirect: '/account/center',
            name: 'account',
            meta: { title: '个人页', icon: 'user', keepAlive: true, permission: [ 'user' ] },
            children: [
              {
                path: '/account/center',
                name: 'center',
                component: () => import('@/views/account/center'),
                meta: { title: '个人中心', keepAlive: true, permission: [ 'user' ] }
              },
              {
                path: '/account/settings',
                name: 'settings',
                component: () => import('@/views/account/settings/Index'),
                meta: { title: '个人设置', hideHeader: true, permission: [ 'user' ] },
                redirect: '/account/settings/base',
                hideChildrenInMenu: true,
                children: [
                  {
                    path: '/account/settings/base',
                    name: 'BaseSettings',
                    component: () => import('@/views/account/settings/BaseSetting'),
                    meta: { title: '基本设置', hidden: true, permission: [ 'user' ] }
                  },
                  {
                    path: '/account/settings/security',
                    name: 'SecuritySettings',
                    component: () => import('@/views/account/settings/Security'),
                    meta: { title: '安全设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
                  },
                  {
                    path: '/account/settings/custom',
                    name: 'CustomSettings',
                    component: () => import('@/views/account/settings/Custom'),
                    meta: { title: '个性化设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
                  },
                  {
                    path: '/account/settings/binding',
                    name: 'BindingSettings',
                    component: () => import('@/views/account/settings/Binding'),
                    meta: { title: '账户绑定', hidden: true, keepAlive: true, permission: [ 'user' ] }
                  },
                  {
                    path: '/account/settings/notification',
                    name: 'NotificationSettings',
                    component: () => import('@/views/account/settings/Notification'),
                    meta: { title: '新消息通知', hidden: true, keepAlive: true, permission: [ 'user' ] }
                  }
                ]
              }
            ]
          }
    

    前端页面显示
    在这里插入图片描述

    展开全文
  • 关于后台管理系统的路由,想花一点时间,彻底的整理一份实现动态路由的点点滴滴。 首先声明,这篇文章是基于花裤衩大神的《手摸手,带你用vue撸后台》,在他项目的基础上,帮助想要实现动态路由的小伙伴,来写的一篇...

    关于后台管理系统的路由,想花一点时间,彻底的整理一份实现动态路由的点点滴滴。

    首先声明,这篇文章是基于花裤衩大神的《手摸手,带你用vue撸后台》,在他项目的基础上,帮助想要实现动态路由的小伙伴,来写的一篇使用笔记。

    https://segmentfault.com/a/1190000009506097#item-3

    为什么要实现动态路由?

    我们在开发后台管理系统的过程中,会有不同的人来操作系统,有admin(管理员)、superAdmin(超管),还会有各种运营人员、财务人员。为了区别这些人员,我们会给不同的人分配不一样的角色,从而来展示不同的菜单,这个就必须要通过动态路由来实现。

    主流的实现方式:

    简单聊一下两种方式的优势,毕竟如果你从来没做过,说再多也看不明白,还是得看代码

    前端控制

    1、不用后端帮助,路由表维护在前端
    2、逻辑相对比较简单,比较容易上手
    

    2、后端控制

    1、相对更安全一点
    2、路由表维护在数据库
    

    一、前端控制

    花裤衩大神的方案是前端控制,他的核心是通过路由的meta属性,通过role来控制路由的加载。具体的实现方案:

    1、根据登录用户的账号,返回前端用户的角色
    
    2、前端根据用户的角色,跟路由表的meta.role进行匹配
    
    3、讲匹配到的路由形成可访问路由
    展开全文
  • 做权限管理,一个核心思想就是后端做权限控制,前端做的所有工作都只是为了提高用户体验,我们不能依靠前端展示或者隐藏一个按钮来实现权限控制,这样肯定是不安全的。 就像用户注册时需要输入邮箱地址,前端校验...

    1. 一个原则

    做权限管理,一个核心思想就是后端做权限控制,前端做的所有工作都只是为了提高用户体验,我们不能依靠前端展示或者隐藏一个按钮来实现权限控制,这样肯定是不安全的。

    就像用户注册时需要输入邮箱地址,前端校验之后,后端还是要校验,两个校验目的不同,前端校验是为了提高响应速度,优化用户体验,后端校验则是为了确保数据完整性。权限管理也是如此,前端按钮的展示/隐藏都只是为了提高用户体验,真正的权限管理需要后端来实现。

    这是非常重要的一点,做前后端分离开发中的权限管理,我们首先要建立上面这样的思考框架,然后在这样的框架下,去考虑其他问题。

    因此,下文我会和大家分享两种方式实现动态菜单,这两种方式仅仅只是探讨如何更好的给用户展示菜单,而不是探讨权限管理,因为权限管理是在后端完成的,也必须在后端完成。

    2、实现方式

    2.1 后端动态返回

    用户登录成功之后,可以查询到用户的角色,再根据用户角色去查询出来用户可以操作的菜单(资源),然后把这些可以操作的资源,组织成一个 JSON 数据,返回给前端,前端再根据这个 JSON 渲染出相应的菜单。

    至于menu表需要保存哪些字段、返回给前端的JSON数据格式怎么组织,还要配合前端同学一起协商。

    这种方式的一个好处是前端的判断逻辑少一些,后端也不算复杂,就是一个 SQL 操作,前端拿到后端的返回的菜单数据,稍微处理一下就可以直接使用了。另外这种方式还有一个优势就是可以动态配置资源-角色以及用户-角色之间的关系,进而调整用户可以操作的资源(菜单)。

    2.2 前端动态渲染

    后端只需要在登录成功后返回当前用户的角色就可以了,剩下的事情则交给前端来做。前端根据角色去动态渲染菜单。不过这种方式有一个弊端就是菜单和角色的关系在前端代码中写死了,以后如果想要动态调整会有一些不方便,可能需要改代码。特别是大项目,权限比较复杂的时候,调整就更麻烦了,所以这种方式一般建议在一些简单的项目中使用。

    本人不太擅长前端,具体实现方式就不展示了,有兴趣可以网上搜一下。

    展开全文
  • 后端路由 在没有出现ajax技术,前后端还没有分离的时候,往往我们通过浏览器访问一个网址时,后端会返回对应网址的完整页面(html+css+js),也就是我们看到的页面,在后端就已经长这样了。那么当一个网站的页面多达几...
  • 为什么设置动态路由 我们在开发的过程中,会有不同的人来操作系统...后端控制 1、相对更安全一点 2、路由表维护在数据库 一、前端控制 通过路由的meta属性role来控制加载路由 具体方法是: 1、根据登录用户...
  • 动态路由 后台管理系统,大部分都会涉及到权限控制这一项需求,即:根据不同登录角色渲染不同页面功能 现在主流有两种方式: 前端控制 逻辑简单,上手快 ...可以参考:动态路由前端控制还是后端控制
  • 1.标准的后端路由 前端的智能设备在发送了网络请求http://IP:port/xxxx时候 后端对xxxx进行判断,后给出响应 是给前端返回一个数据 还是给前端返回一个html页面 2.vue中的路由 vue是一个single page app(单页面的...
  • SpringBoot + Vue3,前后端分离,页面路由控制,登录状态校验,访问控制
  • 一、什么是路由? 对于没有开发过后端,也没有开发过 SPA 的前端来说,「路由」这个名词可能会让人比较困惑,这里的路由并不是指「硬件路由」,也不是网络七层协议中的「网络层路由」,但是其思想原理是一样的。...
  • 前端路由后端路由

    千次阅读 2016-10-01 19:46:03
    后端路由 意味着 浏览器刷新页面。显然很多 webapp 的需求上是不希望这样的体验的。网速慢的话说不定屏幕全白再有新内容。 前端路由就不会有这样的问题了。随意控制,逻辑也可以都放在前端。前端慢慢复杂化,自己的...
  •     公司后台管理系统,需要从后端获取路由表,并正确渲染。结合看到的帖子 基于vue-router的动态权限实现方案,昨天将这个需求实现了。    实现这个问题,有两个关键点。 登陆系统和刷新页面时,使用...
  • 动态路由后端控制

    2021-07-07 15:27:51
    前端这边把路由写好,登录的时候根据用户的角色权限来动态展示路由,(前端控制路由) 详情可参阅花裤衩大佬的项目手把手...,我当时看这个项目看了好久才明白一点逻辑, 因为大神的动态路由那里有好多层判断,并且穿插...
  • 后端路由阶段 因此,后端路由都是由服务器来生成页面,然后返回给客户端,这种方式渲染的页面,不需要单独再加载js或css代码,可以直接交给浏览器显示。 缺点就是 后端开发人员的任务太多太杂,而前端要想处理这...
  • 添加路由守卫, 实现权限控制 router.beforeEach((to, from, next) => { // 1.如果访问的是注册或者登录, 那么就放行 if(to.path === '/login' || to.path === '/register'){ return next(); } // 2.获取当前...
  • react路由权限控制

    千次阅读 2018-07-30 21:37:55
    路由权限控制可以分为两种粒度 一是页面级的粒度,只要在路由跳转的时候做个判断就好了 二是元素级的粒度,这个就需要上react高阶组件了 页面级粒度——dva在model中判断 权限认证和登陆判断一般是分不开...
  • 后端渲染(SSR)后端路由阶段 早期的网站开发整个HTML页面是由服务器来渲染的,服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示。通常是PHP或JSP(Java server page)这里以JSP为例,服务器经过解析存放...
  • 页面登录成功后,执行下面的代码,把后端返回的路由数据用 this.$router.addRoutes() // 添加 this.$router.addRoutes([ { path: '/admin', component: () => import('../views/admin.vue') } ]), // 这样就...
  • 最近开发的一个项目,涉及到这样一个需求:随着项目的不断推进,后台管理功能逐渐增多,与此同时,静态路由表也逐渐扩大,需要把静态路由方式转换为动态路由方式。要完成这样一个转换,有几个技术要点需要解决,其中...
  • iView-DynamicRouter 基于 iview-admin(branch:template),由“后端动态提供路由数据,经前端处理后生成动态路由和菜单”的【后端动态路由模板】,内置“权限管理”业务模型,可任意调整左侧菜单栏、修改其相关权限...
  • 实现效果描述 最近大家都说牛市来了,我也在疯狂的理财,主要买了一些基金,然后做了系统,...后端返回路由结构如下: "user_router": [ { "component": "/dashboard/index", "icon": "form", "id": 14, ..
  • 一个前端看后端路由

    2018-11-29 15:19:31
    但是在例如很多业务情景下就不适用了,例如展示广告,几乎不需要在页面上有其他逻辑,例如严谨的下单流程,后端路由可以严格控制前端不可进入页面,还有后端路由可以应用于API层面提供接口等等许多的场景都是可以的...
  • 后台管理系统项目中的路由需要权限管理,不同的角色登录看到的页面是不一样的,所以路由应该是后端动态返回,然后前端拿到路由表进行处理后调用router.add
  • 很多时候我们在项目的路由都是在前端配置好的 但是有的时候为了进行全面的权限控制,会需要后台给出路由表,...//第一种 可以跟后端小哥约定一个字段,把路由的url后半段直接给后端小哥哥我们直接加载,这里面注意impo
  • // 用户有关的操作控制器 user: { login: () => { console.log('我是login登录逻辑') }, res: () => { console.log('我是reg注册逻辑') } }, // 文章发表的操作控制器 post: { getall: () => {...
  • 采取了前后端分离的开发模式,主要使用技术栈:springboot web部分+vue(路由和axios部分)+token校验 二、详细步骤 第一步 在vue路由定义中多添加自定义字段requireAuth、role、auth: 第二步 设置登录拦截...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,834
精华内容 20,733
关键字:

后端控制路由