精华内容
下载资源
问答
  • javaWeb权限管理控制按钮的两个系统 基于Spring+Spring MVC+Mybatis+MyBatis Generator 版本ssm 基于Spring+Spring MVC+springdata JPA +spring-data-jpa-extra版本sssp
  • 我这个项目有缺点就是权限按钮时,我把它放进了session,方便取出,没有放进缓存! 亲爱的网友们,这是我自己第一次做权限,如果我有什么地方做的不足,望你们多担待,给我指出来,我再进行改正!我的项目仅为参考,...
  • 权限粒度到按钮级别的权限管理系统,系统保护了用户管理、菜单管理、角色管理,并且用shiro组件实现权限管理,粒度细到按钮级别的权限
  • Vue 项目 菜单权限按钮级权限控制

    千次阅读 2020-06-24 12:05:50
    1.页面展示需要鉴权的所有按钮,需要先鉴权菜单权限的显示与隐藏。 2.勾选每个角色或者用户所能看的权限保存在数据库。该权限数据是一个权限字段的数组。 3.全局自定义指令(directive)控制按钮权限数据的方法,...

    实现思路

    1.页面展示需要鉴权的所有按钮,需要先鉴权菜单权限的显示与隐藏。

    2.勾选每个角色或者用户所能看的权限保存在数据库。该权限数据是一个权限字段的数组。

    3.全局自定义指令(directive)控制按钮权限数据的方法,登入时获取后端传来的按钮权限数组。

    4.在每个按钮中调用该指令,并传入该操作的权限字段和后端保存的权限字段进行匹配,能匹配则该操作按钮可显示

    具体代码如下

    1. 获取登录用户所有权限
    // 获取权限菜单(存在store下modules下user.js的actions中)
     getMenuTree ({ commit, state }) {
       return new Promise((resolve, reject) => {
         getMenuTree(state.token).then(response => {
           const data = response.result
           if (!data) {
             reject('验证失败,请重新登录')
           }
           // 存菜单结构
           commit('SET_ROLES', data)
           // 重置按钮权限
           btns = []
           const btn = findAllBtn(data)
           // 存所有按钮权限
           commit('SET_BUTTONS', btn)
           resolve(data)
         }).catch(error => {
           reject(error)
         })
       })
     }
    // 递归获取按钮list
    let btns = []
    export function findAllBtn (list) {
     list.forEach(val => {
     // 与后台协商所有菜单资源(resCode)下的type是1表菜单,2为按钮
       if (val.type === '1') {
         if (val.children && val.children.length > 0) {
           findAllBtn(val.children)
         }
       } else {
         btns.push(val.reCode)
       }
     })
    }
    
    1. 对比菜单权限的方法
    在store下modules下新建一个permission.js(获取最终动态权限菜单)
    /**
    * @param  {Array} userRouter 后台返回的用户权限json
    * @param  {Array} allRouter  前端配置好的所有动态路由的集合
    * @return {Array} realRoutes 过滤后的路由
    */
    export function recursionRouter (userRouter = [], allRouter = []) {
     let realRoutes = []
     allRouter.forEach(val => {
       userRouter.forEach(item => {
         if (val.path.includes('/')) {
           if (item.resCode === val.path.split('/')[1]) {
             val.children = recursionRouter(item.children, val.children)
             realRoutes.push(val)
           }
         } else {
           if (item.resCode === val.path) {
             if (item.children && item.children.length > 0) {
               val.children = recursionRouter(item.children, val.children)
             }
             realRoutes.push(val)
           }
         }
       })
     })
     realRoutes.push({ path: '*', redirect: '/404', isShow: true })
     // console.log(222, realRoutes)
     return realRoutes
    }
    // asyncRouterMap本地配置好的所有动态路由的集合
    const actions = {
     generateRoutes ({ commit }, roles) {
       return new Promise(resolve => {
         let accessedRoutes = recursionRouter(roles, [...asyncRouterMap])
         commit('SET_ROUTES', accessedRoutes)
         resolve(accessedRoutes)
       })
     }
    }
    

    对比菜单权限需要在全局路由守卫中如下操作

    router.beforeEach(async (to, from, next) => {
     // 确定用户是否已登录
     const hasToken = getToken()
     // 判断是否有token
     if (hasToken) {
       // 运营端登录
       if (to.path === '/login') {
         // 如果已登录,则重定向到主页
         next({ path: '/' })
         NProgress.done()
       } else {
         // 确定用户是否已获得了他的权限角色
         const hasRoles = store.getters.roles && store.getters.roles.length > 0
         if (hasRoles) {
           next()
         } else {
           // 角色必须是对象数组
           const roles = await store.dispatch('user/getMenuTree')
             // 根据角色生成可访问路由映射
             const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
             // 清空静态路由
             resetRouter()
             // 动态添加可访问路由
             router.addRoutes(accessRoutes)
             // hack 方法,以确保addRoutes是完整的
             // 设置replace: true,这样导航就不会留下历史记录
             next({ ...to, replace: true })
         }
       }
     } else {
       // 在免登录白名单,直接进入
       if (whiteList.indexOf(to.path) !== -1) {
         next()
       } else {
         // 没有访问权限的其他页面被重定向到登录页面
         next(`/login?redirect=${to.path}`)
         NProgress.done()
       }
     }
    })
    
    1. 全局自定义指令
    // 需要全局注入(即在main.js引入)
    import Vue from 'vue'
    import store from '@/store'
    /**
     * 使用:v-permission="'resCode'"
     *   resCode 按钮资源(即路由path)
     * **/
    Vue.directive('permission', {
      inserted (el, vDir) {
        let btnPermission = store.getters.buttons
        if (vDir.value) {
          if (!btnPermission.includes(vDir.value)) {
            el.parentNode.removeChild(el)
          }
        }
      }
    })
    
    1. 页面中使用
    <el-button type="primary" @click="roleExport" v-permission="'ent-role-export'">导出</el-button>
    

    如图

    初始在这里插入图片描述
    取消权限后
    在这里插入图片描述
    在这里插入图片描述


    相关文章

    基于elementUI中table组件二次封装(Vue项目)


    axios二次封装,接口统一存放,满足RESTful风格


    keep-alive不能缓存多层级路由(vue-router)菜单问题解决


    基于ElementUi再次封装基础组件文档

    展开全文
  • 每天早上七点三十,准时推送干货一、介绍在实际的项目开发过程中,菜单权限功能可以说是后端管理系统中必不可少的一个环节,根据业务的复杂度,设计的时候可深可浅,但无论怎么变化,设计的思路基本都...

    每天早上七点三十,准时推送干货

    一、介绍

    在实际的项目开发过程中,菜单权限功能可以说是后端管理系统中必不可少的一个环节,根据业务的复杂度,设计的时候可深可浅,但无论怎么变化,设计的思路基本都是围绕着用户、角色、菜单进行相应的扩展

    今天小编就和大家一起来讨论一下,怎么设计一套可以精确到按钮级别的菜单权限功能,废话不多说,直接开撸!

    二、数据库设计

    先来看一下,用户、角色、菜单表对应的ER图,如下:

    其中,用户和角色是多对多的关系角色与菜单也是多对多的关系用户通过角色来关联到菜单,当然也有的业务系统菜单权限模型,是可以直接通过用户关联到菜单,对菜单权限可以直接控制到用户级别,不过这个都不是问题,这个也可以进行扩展。

    对于用户、角色表比较简单,下面,我们重点来看看菜单表的设计,如下:

    可以看到,整个菜单表就是一个树型结构,关键字段说明

    • menu_code:菜单编码,用于后端权限控制

    • parent_id:菜单父节点ID,方便递归遍历菜单

    • node_type:节点类型,可以是文件夹、页面或者按钮类型

    • link_url:页面对应的地址,如果是文件夹或者按钮类型,可以为空

    • level:菜单树的层次,以便于查询指定层级的菜单

    • path:树id的路径,主要用于存放从根节点到当前树的父节点的路径,逗号分隔,想要找父节点会特别快

    为了后面方便开发,我们先创建一个名为menu_auth_db的数据库,初始脚本如下:

    CREATE DATABASE IF NOT EXISTS menu_auth_db default charset utf8mb4 COLLATE utf8mb4_unicode_ci;
    
    CREATE TABLE menu_auth_db.tb_user (
      id bigint(20) unsigned NOT NULL COMMENT '消息给过来的ID',
      mobile varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
      name varchar(100) NOT NULL DEFAULT '' COMMENT '姓名',
      password varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
      is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
      PRIMARY KEY (id),
      KEY idx_name (name) USING BTREE,
      KEY idx_mobile (mobile) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
    
    CREATE TABLE menu_auth_db.tb_user_role (
      id bigint(20) unsigned NOT NULL COMMENT '主键',
      user_id bigint(20) NOT NULL COMMENT '用户ID',
      role_id bigint(20) NOT NULL COMMENT '角色ID',
      PRIMARY KEY (id),
      KEY idx_user_id (user_id) USING BTREE,
      KEY idx_role_id (role_id) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色表';
    
    CREATE TABLE menu_auth_db.tb_role (
      id bigint(20) unsigned NOT NULL COMMENT '主键',
      code varchar(100) NOT NULL DEFAULT '' COMMENT '编码',
      name varchar(100) NOT NULL DEFAULT '' COMMENT '名称',
      is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
      PRIMARY KEY (id),
      KEY idx_code (code) USING BTREE,
      KEY idx_name (name) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';
    
    
    CREATE TABLE menu_auth_db.tb_role_menu (
      id bigint(20) unsigned NOT NULL COMMENT '主键',
      role_id bigint(20) NOT NULL COMMENT '角色ID',
      menu_id bigint(20) NOT NULL COMMENT '菜单ID',
      PRIMARY KEY (id),
      KEY idx_role_id (role_id) USING BTREE,
      KEY idx_menu_id (menu_id) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色菜单关系表';
    
    
    CREATE TABLE menu_auth_db.tb_menu (
      id bigint(20) NOT NULL COMMENT '主键',
      name varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
      menu_code varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单编码',
      parent_id bigint(20) DEFAULT NULL COMMENT '父节点',
      node_type tinyint(4) NOT NULL DEFAULT '1' COMMENT '节点类型,1文件夹,2页面,3按钮',
      icon_url varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '图标地址',
      sort int(11) NOT NULL DEFAULT '1' COMMENT '排序号',
      link_url varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '页面对应的地址',
      level int(11) NOT NULL DEFAULT '0' COMMENT '层次',
      path varchar(2500) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '树id的路径 整个层次上的路径id,逗号分隔,想要找父节点特别快',
      is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
      PRIMARY KEY (id) USING BTREE,
      KEY idx_parent_id (parent_id) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单表';
    

    三、后端开发

    菜单权限模块的数据库设计,一般5张表就可以搞定,真正有点复杂的地方在于数据的写入和渲染,当然如果老板突然让你来开发一套菜单权限系统,我们也没必要慌张,下面,我们一起来看看后端应该如何开发。

    3.1、创建项目

    为了方便快捷,小编我采用的是springboot+mybatisPlus组件来快速开发,直接利用mybatisPlus官方提供的快速生成代码的demo,一键生成所需的daoserviceweb层的代码,结果如下:

    3.2、编写菜单添加服务

    @Override
    public void addMenu(Menu menu) {
        //如果插入的当前节点为根节点,parentId指定为0
        if(menu.getParentId().longValue() == 0){
            menu.setLevel(1);//根节点层级为1
            menu.setPath(null);//根节点路径为空
        }else{
            Menu parentMenu = baseMapper.selectById(menu.getParentId());
            if(parentMenu == null){
                throw new CommonException("未查询到对应的父节点");
            }
            menu.setLevel(parentMenu.getLevel().intValue() + 1);
            if(StringUtils.isNotEmpty(parentMenu.getPath())){
                menu.setPath(parentMenu.getPath() + "," + parentMenu.getId());
            }else{
                menu.setPath(parentMenu.getId().toString());
            }
        }
        //可以使用雪花算法,生成ID
        menu.setId(System.currentTimeMillis());
        super.save(menu);
    }
    

    新增菜单比较简单,直接将数据插入即可,需要注意的地方是parent_idlevelpath,这三个字段的写入,如果新建的是根节点,默认parent_id0,方便后续递归遍历。

    3.3、编写菜单后端查询服务

    • 新建一个菜单视图实体类

    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class MenuVo implements Serializable {
    
        private static final long serialVersionUID = -4559267810907997111L;
    
        /**
         * 主键
         */
        private Long id;
    
        /**
         * 名称
         */
        private String name;
    
        /**
         * 菜单编码
         */
        private String menuCode;
    
        /**
         * 父节点
         */
        private Long parentId;
    
        /**
         * 节点类型,1文件夹,2页面,3按钮
         */
        private Integer nodeType;
    
        /**
         * 图标地址
         */
        private String iconUrl;
    
        /**
         * 排序号
         */
        private Integer sort;
    
        /**
         * 页面对应的地址
         */
        private String linkUrl;
    
        /**
         * 层次
         */
        private Integer level;
    
        /**
         * 树id的路径 整个层次上的路径id,逗号分隔,想要找父节点特别快
         */
        private String path;
    
        /**
         * 子菜单集合
         */
        List<MenuVo> childMenu;
    }
    
    • 编写菜单查询服务,使用递归重新封装菜单视图

    @Override
    public List<MenuVo> queryMenuTree() {
        Wrapper queryObj = new QueryWrapper<>().orderByAsc("level","sort");
        List<Menu> allMenu = super.list(queryObj);
     // 0L:表示根节点的父ID
        List<MenuVo> resultList = transferMenuVo(allMenu, 0L);
        return resultList;
    }
    
    /**
     * 封装菜单视图
     * @param allMenu
     * @param parentId
     * @return
     */
    private List<MenuVo> transferMenuVo(List<Menu> allMenu, Long parentId){
        List<MenuVo> resultList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(allMenu)){
            for (Menu source : allMenu) {
                if(parentId.longValue() == source.getParentId().longValue()){
                    MenuVo menuVo = new MenuVo();
                    BeanUtils.copyProperties(source, menuVo);
                    //递归查询子菜单,并封装信息
                    List<MenuVo> childList = transferMenuVo(allMenu, source.getId());
                    if(!CollectionUtils.isEmpty(childList)){
                        menuVo.setChildMenu(childList);
                    }
                    resultList.add(menuVo);
                }
            }
        }
        return resultList;
    }
    
    • 编写一个菜单树查询接口,如下:

    @RestController
    @RequestMapping("/menu")
    public class MenuController {
    
        @Autowired
        private MenuService menuService;
    
        @PostMapping(value = "/queryMenuTree")
        public List<MenuVo> queryTreeMenu(){
            return menuService.queryMenuTree();
        }
    }
    

    为了便于演示,我们先初始化7条数据,如下图:

    其中最后三条是按钮类型,等下会用于后端权限控制,接口查询结果如下:

    这个服务是针对后端管理界面查询的,会将所有的菜单全部查询出来以便于进行管理,展示结果类似如下图:

    这个图片截图于小编正在开发的一个项目,内容可能不一致,但是数据结构基本都是一致的

    3.4、编写用户菜单权限查询服务

    在上面,我们介绍到了用户通过角色来关联菜单,因此,很容易想到,流程如下:

    • 第一步:先通过用户查询到对应的角色;

    • 第二步:然后再通过角色查询到对应的菜单;

    • 第三步:最后将菜单查询出来之后进行渲染;

    实现过程相比菜单查询服务多了前2个步骤,过程如下:

    @Override
    public List<MenuVo> queryMenus(Long userId) {
        //1、先查询当前用户对应的角色
        Wrapper queryUserRoleObj = new QueryWrapper<>().eq("user_id", userId);
        List<UserRole> userRoles = userRoleService.list(queryUserRoleObj);
        if(!CollectionUtils.isEmpty(userRoles)){
            //2、通过角色查询菜单(默认取第一个角色)
            Wrapper queryRoleMenuObj = new QueryWrapper<>().eq("role_id", userRoles.get(0).getRoleId());
            List<RoleMenu> roleMenus = roleMenuService.list(queryRoleMenuObj);
            if(!CollectionUtils.isEmpty(roleMenus)){
                Set<Long> menuIds = new HashSet<>();
                for (RoleMenu roleMenu : roleMenus) {
                    menuIds.add(roleMenu.getMenuId());
                }
                //查询对应的菜单
                Wrapper queryMenuObj = new QueryWrapper<>().in("id", new ArrayList<>(menuIds));
                List<Menu> menus = super.list(queryMenuObj);
                if(!CollectionUtils.isEmpty(menus)){
                    //将菜单下对应的父节点也一并全部查询出来
                    Set<Long> allMenuIds = new HashSet<>();
                    for (Menu menu : menus) {
                        allMenuIds.add(menu.getId());
                        if(StringUtils.isNotEmpty(menu.getPath())){
                            String[] pathIds = StringUtils.split(",", menu.getPath());
                            for (String pathId : pathIds) {
                                allMenuIds.add(Long.valueOf(pathId));
                            }
                        }
                    }
                    //3、查询对应的所有菜单,并进行封装展示
                    List<Menu> allMenus = super.list(new QueryWrapper<Menu>().in("id", new ArrayList<>(allMenuIds)));
                    List<MenuVo> resultList = transferMenuVo(allMenus, 0L);
                    return resultList;
                }
            }
    
        }
        return null;
    }
    
    • 编写一个用户菜单查询接口,如下:

    @PostMapping(value = "/queryMenus")
    public List<MenuVo> queryMenus(Long userId){
     //查询当前用户下的菜单权限
        return menuService.queryMenus(userId);
    }
    

    有的同学,可能觉得没必要存放path这个字段,的确在某些场景下不需要。

    为什么要存放这个字段呢?

    小编在跟前端进行对接的时候,发现这么一个问题,有些前端的树型组件,在勾选子集的时候,不会将对应的父ID传给后端,例如,我在勾选【列表查询】的时候,前端无法将父节点【菜单管理】ID也传给后端,所有后端实际存放的是一个尾节点,需要一个字段path,来存放节点对应的父节点路径。

    其实,前端也可以传,只不过需要修改组件的属性,前端修改完成之后,树型组件就无法全选,不满足业务需求。

    所以,有些时候得根据实际得情况来进行取舍。

    3.5、编写后端权限控制

    后端进行权限控制目标,主要是为了防止无权限的用户,进行接口请求查询。

    其中菜单编码menuCode就是一个前、后端联系的桥梁,细心的你会发现,所有后端的接口,与前端对应的都是按钮操作,所以我们可以以按钮为基准,实现前后端双向控制

    以【角色管理-查询】这个为例,前端可以通过菜单编码实现是否展示这个查询按钮,后端可以通过菜单编码来判断,当前用户是否具备请求接口的权限。

    以后端为例,我们只需编写一个权限注解和代理拦截器即可!

    • 编写一个权限注解

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CheckPermissions {
    
        String value() default "";
    }
    
    • 编写一个代理拦截器,拦截有@CheckPermissions注解的方法

    @Aspect
    @Component
    public class CheckPermissionsAspect {
    
        @Autowired
        private MenuMapper menuMapper;
    
        @Pointcut("@annotation(com.company.project.core.annotation.CheckPermissions)")
        public void checkPermissions() {}
    
        @Before("checkPermissions()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            Long userId = null;
            Object[] args = joinPoint.getArgs();
            Object parobj = args[0];
            //用户请求参数实体类中的用户ID
            if(!Objects.isNull(parobj)){
                Class userCla = parobj.getClass();
                Field field = userCla.getDeclaredField("userId");
                field.setAccessible(true);
                userId = (Long) field.get(parobj);
            }
            if(!Objects.isNull(userId)){
                //获取方法上有CheckPermissions注解的参数
                Class clazz = joinPoint.getTarget().getClass();
                String methodName = joinPoint.getSignature().getName();
                Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
                Method method = clazz.getMethod(methodName, parameterTypes);
                if(method.getAnnotation(CheckPermissions.class) != null){
                    CheckPermissions annotation = method.getAnnotation(CheckPermissions.class);
                    String menuCode = annotation.value();
                    if (StringUtils.isNotBlank(menuCode)) {
                        //通过用户ID、菜单编码查询是否有关联
                        int count = menuMapper.selectAuthByUserIdAndMenuCode(userId, menuCode);
                        if(count == 0){
                            throw new CommonException("接口无访问权限");
                        }
                    }
                }
            }
        }
    }
    
    • 我们以【角色管理-查询】为例,先新建一个请求实体类RoleDto,添加用户ID属性

    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    public class RoleDto extends Role {
    
     //添加用户ID
        private Long userId;
    }
    
    • 在需要的接口上,添加@CheckPermissions注解,增加权限控制

    @RestController
    @RequestMapping("/role")
    public class RoleController {
    
        private RoleService roleService;
    
        @CheckPermissions(value="roleMgr:list")
        @PostMapping(value = "/queryRole")
        public List<Role> queryRole(RoleDto roleDto){
            return roleService.list();
        }
    
        @CheckPermissions(value="roleMgr:add")
        @PostMapping(value = "/addRole")
        public void addRole(RoleDto roleDto){
            roleService.add(roleDto);
        }
    
        @CheckPermissions(value="roleMgr:delete")
        @PostMapping(value = "/deleteRole")
        public void deleteRole(RoleDto roleDto){
            roleService.delete(roleDto);
        }
    }
    

    依次类推,当我们想对某个接口进行权限控制的时候,只需要添加一个注解@CheckPermissions,并填写对应的菜单编码即可!

    四、用户权限测试

    我们先初始化一个用户【张三】,然后给他分配一个角色【访客人员】,同时给这个角色分配一下2个菜单权限【系统配置】、【用户管理】,等会用于权限测试。

    初始内容如下:

    数据初始化完成之后,我们来启动项目,传入用户【张三】的ID,查询用户具备的菜单权限,结果如下:

    查询结果,用户【张三】有两个菜单权限!

    接着,我们来验证一下,用户【张三】是否有角色查询权限,请求角色查询接口如下:

    因为没有配置角色查询接口,所以无权访问!

    五、总结

    整片内容,只介绍了后端关键的服务实现过程,可能也有遗漏的地方,欢迎网友点评、吐槽!

    最后说两句(求关注)

    最近大家应该发现微信公众号信息流改版了吧,再也不是按照时间顺序展示了。这就对阿粉这样的坚持的原创小号主,可以说非常打击,阅读量直线下降,正反馈持续减弱。

    所以看完文章,哥哥姐姐们给阿粉来个在看吧,让阿粉拥有更加大的动力,写出更好的文章,拒绝白嫖,来点正反馈呗~。

    如果想在第一时间收到阿粉的文章,不被公号的信息流影响,那么可以给Java极客技术设为一个星标

    最后感谢各位的阅读,才疏学浅,难免存在纰漏,如果你发现错误的地方,由于本号没有留言功能,还请你在后台留言指出,我对其加以修改。

    最后谢谢大家支持~

    最最后,重要的事再说一篇~

    快来关注我呀~
    快来关注我呀~
    快来关注我呀~

    < END >

    如果大家喜欢我们的文章,欢迎大家转发,点击在看让更多的人看到。也欢迎大家热爱技术和学习的朋友加入的我们的知识星球当中,我们共同成长,进步。

    展开全文
  • 动态菜单和按钮控制-前端权限控制-RBAC模型 目录 文章目录1、前文回顾2、动态菜单2.1、原理2.2、对比发现问题2.3 改造3、按钮控制3.1、原理...数据库中给不同用户赋予不同的菜单权限, 用户登录获取菜单列表 前端路由

    动态菜单和按钮控制-前端权限控制-RBAC模型


    目录




    内容

    1、前文回顾

    要实现动态菜单和按钮控制,需要菜单和按钮权限数据,这些我们在之前的博文中已实现。
    相关博文如下:

    1. 层级菜单生成算法
    2. 菜单和按钮权限数据封装返回

    2、动态菜单

    2.1、原理

    1. 数据库中给不同用户赋予不同的菜单权限,
    2. 用户登录获取菜单列表
    3. 前端路由加载菜单

    2.2、对比发现问题

    菜单的生成依赖与前端路由控制。

    • 框架默认路由对象:

        {
        	path: "/zip",
        	component: Layout,
        	redirect: "/zip/download",
        	alwaysShow: true,
        	name: "Zip",
        	meta: { title: "压缩", icon: "zip" },
        	children: [
        	  {
        		path: "download",
        		component: () => import("@/views/zip/index"),
        		name: "ExportZip",
        		meta: { title: "导出压缩" }
        	  }
        	]
          }
      
    • 后端返回的菜单对象:

        {
        	"id": "1352785556979511297",
        	"name": "companyList",
        	"path": "companyList",
        	"type": 1,
        	"orderNum": 1,
        	"pid": "1352785556979511296",
        	"hidden": false,
        	"meta": {
        	  "icon": "company",
        	  "title": "公司管理"
        	}
         }
      

    小伙伴们发现了什么不吗?

    1. 是的我们自己构造的菜单对象中缺少路由对应的组件,即component属性。我们需要在控制动态菜单生成的地方自己构建。
    2. 原有路由是通过菜单对象 meta{roles: [角色]}实现动态菜单,而我们通过用户具有的菜单权限来控制。

    通过查找我们发现路由对象数组生成方法为generateRoutes,方法位于@/store/modules/permission.js中。

    2.3 改造

    • 代码如下:

        generateRoutes({ commit }, menus) {
        	return new Promise(resolve => {
        	  // let accessedRoutes
        	  // if (roles.includes('admin')) {
        	  //   accessedRoutes = asyncRoutes || []
        	  // } else {
        	  //   let accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
        	  // // }
        	  // 生成路由
        	  let accessedRoutes
        	  menus.forEach(v => {
        		v.component = Layout
        		if(v.children && v.children.length > 0) {
        		  v.children.forEach(c => {
        			c.component = (resolve) => require(["@/views/" + v.name + "/" + c.name],resolve)
        		  })
        		  // "@/views/" + v.name + "/" + c.name
        		}
        	  })
        	  accessedRoutes = menus
        	  commit('SET_ROUTES', accessedRoutes)
        	  resolve(accessedRoutes)
        	})
          }
      
    • 解析:

      • 原有component通过import 来动态烂加载路由组件,这里我们构建后会报错,具体原理不是很清楚,我们改又ES5语法require实现

    3、按钮控制

    3.1、原理

    1. 数据库不同用户赋予不同的按钮权限-字符串标志
    2. 用户登录获取按钮权限数组
    3. 判断是否又权限,按钮组件v-if来控制显示与否

    3.2、实现

    因为登录之后又很多页面,每个页面又很多的按钮,而判断逻辑都一样,所有我们把判断逻辑封装为方法,挂在到Vue示例上,具体代码如下:

    import store from './store'
    ...
    Vue.prototype.$hasPerm = (perm) => {
      let btns = store.getters.btns
      if (btns && btns.length > 0) {
       return btns.includes(perm)
      }
      return false
    }
    

    这里简单列举一个按钮示例:

     <el-button
    		class="filter-item"
    		style="margin-left: 10px;"
    		type="primary"
    		icon="el-icon-edit"
    		@click="handleCreate"
    		v-if="$hasPerm('sys:company:save')"
    	  >
    		添加
    </el-button>
    

    4、效果展示与问题

    4.1、效果展示

    • 管理员用户登录界面:
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpWvi7YQ-1611749770257)(./images/2021-01-27_ret-auth-admin.png)]

    • 其他某个用户登录:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lSQA8v9U-1611749770266)(./images/2021-01-27_ret-auth-normal.png)]

    4.2、问题

    1. main.js作为入口,代码越简洁越好,我们把判断按钮显示与否的方法写在main.js中
    2. 按钮权限控制,我们是在具体的每个按钮组件处判断,且与权限标志字符串,比如’sys:company:save’耦合在一起,不利于后期修改

    问题2暂时没想到比较好的解决办法,问题1后面有空优化以下。其中非重要的页面列表和详情,默认所有用户都可以查看,即没有加权限。

    后记

    本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785

    后端JAVA源代码地址:https://gitee.com/gaogzhen/ihrm-parent    // 后端项目
    前端项目源代码地址:https://gitee.com/gaogzhen/ihrm-vue    // 前端后台管理系统
    
    展开全文
  • Java访问权限控制源代码 1个目标文件 摘要:Java源码,文件操作,权限控制 Java访问权限控制,为Java操作文件、写入文件分配合适的权限,定义写到文件的信息、定义文件,输出到c:/hello.txt、写信息到文件、关闭输出流...
  • easyui后台管理系统,包括菜单权限按钮权限,后台使用springMVC+hibernate,mysql数据库,jsp
  • 基于RBAC思想的权限控制,可先建立完整的库,也可以使用使用代码生成,包中提供两种方式, 代码先后顺序 菜单控制----》元素控制-------》url控制
  • 菜单访问权限控制 配置helloword菜单【系统管理】-【菜单管理】 其中前端组件配置相对src/views/目录下的 目录名+文件名 例如页面src/views/jeecg/helloworld.vue 前端组件配置 jeecg/helloworld 用户角色授权...

    一.菜单访问权限控制

    配置helloword菜单【系统管理】-【菜单管理】 输入图片说明

    • 其中前端组件配置相对src/views/目录下的 目录名+文件名
    • 例如页面src/views/jeecg/helloworld.vue 前端组件配置 jeecg/helloworld

    输入图片说明 用户角色授权【系统管理】-【角色管理】-授权 输入图片说明输入图片说明 点击菜单访问页面展示Hello World!

    二.表单访问权限控制

    禁用控制用法二

    一、用法

    (1)页面引入工具js

    import { disabledAuthFilter } from "@/utils/authFilter"
    

    (2)methods方法中实现:

    isDisabledAuth(code){
          return disabledAuthFilter(code);
        },
    

    输入图片说明

    输入图片说明

    (2)权限控制代码示例:

    
    <a-input-number :disabled="isDisabledAuth('name')"   v-decorator="[ 'days', {}]" />
    

    二、权限配置:

    输入图片说明

    三、使用说明

    • :disabled="isDisabledAuth('name')"调用方法disabledAuth,方法参数“name”为授权标识,该方法根据授权规则返回true/false,控制是否禁用
    • 权限编码在【系统管理--菜单管理】中配置,添加按钮类型的菜单数据,授权标识配置值“name”,策略选择可编辑,状态选择有效
    • 控制规则:
    • (1)菜单权限中若没有对应指令编码的配置,则不进行禁用控制,
    • (2)权限配置无效状态时,则不进行权限控制,有效状态时进行控制
    • (3)策略:可编辑,未授权时控件禁用,授权后可编辑

    四、流程节点权限

    (1)说明:

    • 节点权限配置优先级高于菜单权限配置
    • 节点权限应用于使用组件方式加载的附加表单页面,并对附加表单页面进行权限控制
    • 显示控制用法见上面用法描述
    • 节点权限是通过 props: ['formData'],来传递给节点表单页面的,因此页面一定要定义这个,否则,节点配置的权限不生效,节点表单开发方法见【流程节点对接表单页面开发方法】

    • 权限配置无效状态时,则不进行权限控制,有效状态时进行控制

    • (2)methods方法中实现:
      isDisabledAuth(code){
            return disabledAuthFilter(code,this.formData);
          },
      

    (2)权限配置: 在【流程管理-流程设计】中找到需要配置的流程,进入【流程配置-流程节点】选择需要进行权限控制的节点, 点击【更多-权限设置】,新增/编辑 来配置权限。

    输入图片说明

    三.页面按钮权限用法

    页面按钮权限用法

    1.前端页面通过使用指令 v-has

    <a-button @click="handleAdd" v-has="'user:add'" type="primary" icon="plus">添加用户</a-button>
    

    2.后台进入菜单管理页面配置按钮权限菜单 输入图片说明

    3.进入角色管理授权按钮(授权后即可看见按钮) 输入图片说明

    四.JAVA访问权限控制

    1.后台请求权限控制,通过Shiro注解 @RequiresPermissions

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    @RequiresPermissions("user:add")
    public Result<SysUser> add(@RequestBody JSONObject jsonObject) {
    

    2.后台进入菜单管理页面配置访问权限标识(选择按钮类型) (配置方式与按钮权限一样,即同一个授权标识,可以同时控制后台请求和前台按钮显示控制) 输入图片说明

    3.进入角色管理授权访问权限(授权后即可访问该请求) 输入图片说明

    五.数据权限规则篇

     

    1、功能说明

    列表数据权限,主要通过数据权限控制行数据,让不同的人有不同的查看数据规则; 比如: 销售人员只能看自己的数据;销售经理可以看所有下级销售人员的数据;财务只看金额大于5000的数据等等;

    2、数据权限分两大类型

    序号类型规则字段区别说明
    1编码方式规则字段是驼峰写法,对应mybatis实体的字段编码模式(通过代码生成器生成代码)
    2Online方式规则字段是下划线写法,对应表的字段Online模式(在线表单模式,无代码)
    规则字段配置说明(非常重要): 
    ①条件规则:大于/大于等于/小于/小于等于/等于/包含/模糊/不等于
    ②规则值:指定值 ( 固定值/系统上下文变量 )
    

    3、数据权限规则篇

    1.当前用户上下文变量

    注意:数据权限配置,规则值可以填写系统上下文变量(当前登录人信息),从而根据当前登录人信息进行权限控制。

    编码描述
    sys_user_code当前登录用户登录账号
    sys_user_name当前登录用户真实名称
    sys_date当前系统日期
    sys_time当前系统时间
    sys_org_code当前登录用户部门编号
    sysMultiOrgCode当前登录用户拥有的所有机构编码,逗号分隔

    规则值,配置写法如下:#{sys_user_code}

    2.建表规范(系统标准字段)

    如果需要通过当前登录人,进行数据权限控制,则业务表必须有以下系统标准字段;数据添加和编辑,jeecg会通过拦截器自动注入操作人的信息。 比如:创建人,创建时间,创建人所属部门、创建人所属公司,有了这些标准字段,就可以通过当前登录人进行数据隔离控制;

    字段英文名字段中文名
    CREATE_BY系统用户登录账号
    CREATE_NAME系统用户真实名字
    SYS_ORG_CODE登录用户所属部门

    3.组织机构邮编规则

    JEECG组织机构支持无限层级,上下级关系通过组织机构编码实现,组织机构编码规则类似邮编方式,看下图; 邮编规则优势: 邮编规则,上下级编码固定规律,便于定位下级和上级; 输入图片说明

    六.系统数据权限用法

     

    1、功能说明

    列表数据权限,主要通过数据权限控制行数据,让不同的人有不同的查看数据规则; 比如: 销售人员只能看自己的数据;销售经理可以看所有下级销售人员的数据;财务只看金额大于5000的数据等等;

    2、使用说明 (有两种使用方法,以下说明以用户管理列表查询示例 配置数据规则:只查询用户账号带1的用户

    方法A步骤如下:

    • A-1.新增权限菜单:进入【系统管理】-->【菜单管理】界面 新增一个权限菜单(如下图)

    输入图片说明

    • A-2.配置数据权限规则:找到上述1新增的菜单,点击操作列更多中的数据规则,配置,只查询用户账号带1的用户(如下图) 输入图片说明
    • A-3.角色授权:进入【系统管理】-->【角色管理】界面找到当前用户对应的角色,点击 更多->授权 操作,右侧弹出框中找到上述1菜单,点击后勾选权限规则,保存(如下图) 输入图片说明
    • A-4.在后台请求方法上加注解@PermissionData在方法上加注解是为了提高系统运行效率,这样就可以指定请求走权限过滤的逻辑,而非一棍子打死,让所有请求都去筛选一下权限(如下图) 输入图片说明
    • A-5.测试,访问用户管理界面发现数据被过滤了,即权限生效!

    方法A的问题在于,每个请求都需要配置一个权限菜单,这样其实也很费劲,同时对于菜单管理也不是很好,鉴于此可以考虑使用方法B 方法B基于注解属性pageComponent,步骤如下:

    • B-1.找到需要配置权限的页面菜单,这里是用户管理菜单

    输入图片说明 直接在该菜单上配置数据规则(如A-2)

    • B-2.角色授权(如A-3)
    • B-3.添加注解 (如A-4,不同的是注解上增加了一个属性)@PermissionData(pageComponent="system/UserList")pageComponent的值和B-1中菜单的前端组件值保持一致
    • B-4.测试,访问用户管理界面发现数据被过滤了,即权限生效!
    规则字段配置说明(非常重要): 
    ①条件规则:大于/大于等于/小于/小于等于/等于/包含/模糊/不等于/自定义SQL
    ②规则值:指定值 ( 固定值/系统上下文变量 )
    

    3、数据权限规范说明

    1.系统上下文变量

    注意:数据权限配置,规则值可以填写系统上下文变量(当前登录人信息),从而根据当前登录人信息进行权限控制。

    编码描述
    sys_user_code当前登录用户登录账号
    sys_user_name当前登录用户真实名称
    sys_date当前系统日期
    sys_time当前系统时间
    sys_org_code当前登录用户部门编号

    规则值,配置写法如下:#{sys_user_code}

    2.建表规范(系统标准字段)

    如果需要通过当前登录人,进行数据权限控制,则业务表必须有以下系统标准字段;数据添加和编辑,jeecg会通过拦截器自动注入操作人的信息。 比如:创建人,创建时间,创建人所属部门、创建人所属公司,有了这些标准字段,就可以通过当前登录人进行数据隔离控制;

    字段英文名字段中文名
    CREATE_BY系统用户登录账号
    CREATE_NAME系统用户真实名字
    SYS_ORG_CODE登录用户所属部门

    3.组织机构邮编规则

    JEECG组织机构支持无限层级,上下级关系通过组织机构编码实现,组织机构编码规则类似邮编方式,看下图; 邮编规则优势: 邮编规则,上下级编码固定规律,便于定位下级和上级; 输入图片说明

     

    展开全文
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 本套Java面试题大全,全的不能再全,哈哈~ 一、Java 基础 1. JDK 和 JRE 有什么区别? JDK:Java ...
  • 接下来跟大家聊聊JavaWeb中权限控制,往大的方向说可以聊聊整合应用系统中的权限控制。在聊权限控制之前先跟大家聊聊RBAC。那么什么是RBAC呢?RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过...
  • 在一个基于SpringMVC的框架中,如何去控制某一个页面中具体Button的权限?...具体页面中按钮权限如何控制? 主要能把思路讲清楚就好,能附一小部分代码那就是最好了。 周五下班前(19:00)结贴。 期待高人答复!!
  • java权限控制

    热门讨论 2012-02-02 00:05:50
    历史上最全的java权限控制 基础S2SH的,基于servlet的,基于spring的,基于struts2的,很多,...有角色权限,角色组,数据权限,操作权限,查询权限,菜单权限按钮权限控制,粗粒度的,细粒度的,很多,欢迎下载....
  • Java 实现系统权限控制思路

    万次阅读 热门讨论 2016-12-19 14:47:45
    4、角色菜单权限表 role_menu; 5、用户菜单权限表 user_menu; 如图: 根据用户角色取出该角色所有权限,并对用户进行权限分配;注意菜单的按钮(新增、删除、修改)权限是放在中间表(user_menu)中的; 1、...
  • springmvc 按钮级别的权限控制

    万次阅读 2017-12-07 09:46:24
    随着企业信息化的不断深入,各种各样的信息系统成为提高企业运营及管理效率的必备工具,越来越多的企业核心机密如销售机会、客户资料、设计方案等通过信息系统存储、备案、流转,这些核心资料一旦外泄,势必对企业...
  • iView admin2.0 从后端数据加载路由实现。包含前端变动代码、数据库脚本、java路由权限组装工具类代码
  • 在单体应用架构下,常见的用户-角色-菜单权限控制模式,譬如shiro,就是在每个接口方法上加RequireRole,RequirePermission,当调用到该方法时,可以从配置的数据库、缓存中来进行匹配,通过这种方式来进行的权限...
  • 实现过程: 1、将菜单记录从数据库中查出,放入Set对象中; 2、对菜单记录进行排序; 3、创建一颗多叉树; 4、对树进行先根遍历,将遍历过程中获得的菜单放到集合对象List中。
  • java实现角色权限菜单树结构

    千次阅读 2020-07-10 14:19:55
    通过迭代器和递归实现查询用户角色的权限树 业务层 /** * @author * @date 2020-07-09 15:36:12 * @description 根据id查找用户权限信息 */ @Override public SysUserDTO getUserMenuDetail(String id) { ...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java访问权限控制,为Java操作文件、写入文件分配合适的权限,定义写到文件的信息、定义文件,输出到c:/hello.txt、写信息到文件、关闭输出流。 Java绘制图片火焰效果 1个目标文件 摘要:Java源码,图形操作,火焰...
  • 通用权限,粒度到按钮,java,spring,mybatis
  • java系统权限设计思路总结

    千次阅读 2018-09-30 22:36:02
    这篇文章的定位,不是宣传某个框架,仅仅之是...1.什么是权限,程序员理解的权限和客户所理解的权限是不是一致的。 2.权限的划分原则,权限到底是根据什么原则进行组合的。 3.角色是用户与权限之间的必要的关...
  • 使用angular 实现简单 用户-角色-菜单-按钮权限控制
  • Java Web权限管理设计及实现

    万次阅读 多人点赞 2018-01-28 13:37:51
    最近在做一个权限相关的功能,在项目原有权限管理上面进行扩展,一方面支持界面上控制按钮级别,后端接口没有权限不能进行访问;另一个方面,对项目中应用管理模块的应用管理员授权,使其具有对其名下的应用添加...
  • Java利用Mybatis进行数据权限控制

    千次阅读 2019-06-06 18:19:18
    权限控制主要分为两块,认证(Authentication)与授权(Authorization)。认证之后确认了身份正确,业务系统就会进行授权,现在业界比较流行的模型就是RBAC(Role-Based Access Control)。RBAC包含为下面四个要素:...
  • 权限菜单设计

    千次阅读 2017-02-24 16:17:05
    关于权限菜单设计 http://blog.csdn.net/bearyb1982/article/details/2448301 权限设计(初稿)   1. 前言:   权限管理往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断...
  • SpringSecurity实现完整动态权限菜单 在实际开发中,开发任何一套系统,基本都少不了权限管理这一块。这些足以说明权限管理的重要性。其实SpringSecurity去年就学了,一直没有时间整理,用了一年多时间了,给我的...
  • 本系统采用B/S架构,MySql数据库,SSM后端框架,完美整合权限管理模块,案例包含用户、角色、菜单按钮信息配置,个性化权限分配,用户体验极高的设计相信深得您们青睐。完整源码,简单部署,功能完整可直接使用,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,846
精华内容 9,138
关键字:

java菜单权限设计控制按钮

java 订阅