若依框架-Vue实用框架(权限控制和页面渲染)(四)

发布时间 2023-03-29 08:58:52作者: 爵岚

Vue实用框架(权限控制和页面渲染)

路由的组成

前端token获取那一步中有一块内容,只是简单提了一下,但其实实际涉及到的内容很多:

 

用户信息的获取

第一步的GetInfo后端接口不讲了,因为接口都比较简单,就根据获取得到的数据展开下:

前端权限控制粒度

 

 

 依旧挑重点讲,user对象的无非就是包含了部门dept和角色roles信息,但有个permissions: 就拿system:user:resetPwd来说,system代表一级路由的关键词(也是路由地址),user是二级路由的关键词,resetPwd是对这个页面中某些操作的描述关键词,具体看下图:

 

 

 

 

 

 

这套权限控制前端有按钮粒度级别的程度,把握得死死的,那么前端代码是如何根据此permissions控制的,继续看代码,随便拉一个页面:

 

 

 有个自定义方法 hasPermi ,顺藤摸瓜看下有个 hasPerm.js ,说明我已经写在注释里了:

 

 

 这个钩子函数需要全局定义:

//全局定义语法参考
Vue.directive('hello', {
    inserted(el) {
        console.log(el);
    }
})

如果是在html中定义,那么可以这么写:

Vue.directive("hello",function(el,binding,vnode){
  el.style["color"]= binding.value;
})

因为这个hasPermi很多地方都会用到,所以肯定采用第一种方式:

 

后端权限控制粒度

如果你认为仅仅只是前端通过控制按钮的显隐来限制操作权限那就太天真了,不信的话我们试一下,把前端按钮保留不刷新,但同时在其他浏览器把该角色的某个按钮权限去除(修改数据库没用,因为取的是redis缓存),那么即使有编辑入口,接口也不会允许通过

 看后端代码:

 @PreAuthorize是security中用来进行权限控制的注解, @ss.hasPermi('system:notice:edit') 其中的表达式,返回boolean类型:

/**
 * 验证用户是否具备某权限
 *
 * @param permission 权限字符串
 * @return 用户是否具备某权限
*/
public boolean hasPermi(String permission) {
    if (StringUtils.isEmpty(permission)) {
      return false;
    }
    //通过request头部中的token获取redis中的User信息
    LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
    if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
      //如果缓存中无用户信息或者该用户也没权限信息,那么直接返回false
      return false;
    }
    //判断是否包含该权限
    return hasPermissions(loginUser.getPermissions(), permission);
}

如果是false则会直接返回403错误,前段会根据返回code弹出对应的错误:

export default {
  '401': '认证失败,无法访问系统资源',
  '403': '当前操作没有权限',
  '404': '访问资源不存在',
  'default': '系统未知错误,请反馈给管理员'
}

后端接口的权限判断都是基于redis缓存中的数据,所以测试权限这一块的时候修改数据库的值是没用的,好在权限的功能模块已经相对完善,能够满足复杂的权限管理,基本上不需要去修改什么。

权限数据解析

再看下第二步的生成动态路由部分:

看下这个路由内部是个什么玩样儿,输出看一下:

 

 相信其他都好理解,就是有个component怎么会有这么多内容: 

// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return asyncRouterMap.filter(route => {
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
    if (route.component) {
      // Layout ParentView 组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'ParentView') {
        route.component = ParentView
      } else {
        //这里的component是一个url,所以直接调用路由懒加载方法注册路由
        route.component = loadView(route.component)
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
      delete route['children']
      delete route['redirect']
    }
    return true
  })
}

可以看到,如果入参component那么会被替换成一个Layout组件,这个组件来自 import Layout from '@/layout/index' ,是决定整个前端布局的页面组件:

 

 

 

 具体前端代码自己去看下,我们重点讲一下其他权限数据的获取以及参数意义,在vue-element-admin官方文档中对各个参数其实已经有很详细的解析:

 所以针对此框架,ruoyi也是配合该文档的:

如果对vue-element-admin是如何根据路由router去渲染menu栏感兴趣,自行看下layout组件中的Sidebar以及Navbar子组件,熟悉vue组件封装的肯定能看懂,不懂vue的出门右转B站学习,因为内容比较多,这里就不多做解释了

那关于通过后台接口获取权限menu数据也很简单,就是一个多表联查,然后在service层对数据进行过滤和组装,拼成前端想要的格式:

 

数据权限解析

在角色编辑页面,有一栏数据权限,里面定义了5种类型

 

 看到关键词【部门】,可能第一反应是觉得部门跟权限之间有直接关系,但根据对数据库表的结构分析来看,并没有:

实际上关于这5种数据权限的解释是这样的:

  • 全局数据权限:超级管理员的概念,拥有所有数据的权限
  • 自定数据权限:需要哪几个就哪几个,默认分配方式,按部门
  • 本部门数据权限:本部门下的所有角色拥有的数据权限
  • 本部门及以下数据权限:本部门及子部门下的数据权限
  • 仅本人数据权限:本人数据

注意,上述权限是数据权限,不是左侧menu栏的权限控制

要验证上述观点,看下代码就行了:

权限数据接口切面

有个切面 DataScopeAspect ,使用注解 @DataScope 去标记, @Before 会对标记该注解的方法进行解析,具体操作如下:

 

 

 

 注解的主要作用就是根据用户的数据权限值来影响最终运行的sql,骚,真的骚:

 

 所以数据权限也没有我们想象中这么智能,还是要在对应的接口代码中手动加注解对应表的别名以及xml中的数据范围过滤,不是自动过滤