博客信息

通用权限设计方案

发布时间:『 2019-06-18 22:22』  博客类别:解决方案  阅读(2051)

前言

每个系统都有权限模块,权限就是针对于系统资源(菜单、按钮等等)进行统一管理;

让不同的用户能够使用同一个系统中的不同资源;

本篇博客就给大家介绍一种通用的系统权限设计方案,当然权限设计的方式有很多种,这只是其中的一种。本篇博客设计到的主要技术有springbootbootstrapshiro等。

效果图


小李飞刀_解决方案


小李飞刀_解决方案


小李飞刀_解决方案



表设计

小李飞刀_解决方案


小李飞刀_解决方案


小李飞刀_解决方案


小李飞刀_解决方案


小李飞刀_解决方案


小李飞刀_解决方案



核心代码

左侧树功能

实体类定义


package com.javaxl.bootdo.system.domain;

import java.io.Serializable;
import java.util.Date;

public class MenuDO implements Serializable {
   private static final long serialVersionUID = 1L;
   //
   private Long menuId;
   // 父菜单ID,一级菜单为0
   private Long parentId;
   // 菜单名称
   private String name;
   // 菜单URL
   private String url;
   // 授权(多个用逗号分隔,如:user:list,user:create)
   private String perms;
   // 类型 0:目录 1:菜单 2:按钮
   private Integer type;
   // 菜单图标
   private String icon;
   // 排序
   private Integer orderNum;
   // 创建时间
   private Date gmtCreate;
   // 修改时间
   private Date gmtModified;

   /**
    * 设置:
    */
   public void setMenuId(Long menuId) {
      this.menuId = menuId;
   }

   /**
    * 获取:
    */
   public Long getMenuId() {
      return menuId;
   }

   /**
    * 设置:父菜单ID,一级菜单为0
    */
   public void setParentId(Long parentId) {
      this.parentId = parentId;
   }

   /**
    * 获取:父菜单ID,一级菜单为0
    */
   public Long getParentId() {
      return parentId;
   }

   /**
    * 设置:菜单名称
    */
   public void setName(String name) {
      this.name = name;
   }

   /**
    * 获取:菜单名称
    */
   public String getName() {
      return name;
   }

   /**
    * 设置:菜单URL
    */
   public void setUrl(String url) {
      this.url = url;
   }

   /**
    * 获取:菜单URL
    */
   public String getUrl() {
      return url;
   }

   /**
    * 设置:授权(多个用逗号分隔,如:user:list,user:create)
    */
   public void setPerms(String perms) {
      this.perms = perms;
   }

   /**
    * 获取:授权(多个用逗号分隔,如:user:list,user:create)
    */
   public String getPerms() {
      return perms;
   }

   /**
    * 设置:类型 0:目录 1:菜单 2:按钮
    */
   public void setType(Integer type) {
      this.type = type;
   }

   /**
    * 获取:类型 0:目录 1:菜单 2:按钮
    */
   public Integer getType() {
      return type;
   }

   /**
    * 设置:菜单图标
    */
   public void setIcon(String icon) {
      this.icon = icon;
   }

   /**
    * 获取:菜单图标
    */
   public String getIcon() {
      return icon;
   }

   /**
    * 设置:排序
    */
   public void setOrderNum(Integer orderNum) {
      this.orderNum = orderNum;
   }

   /**
    * 获取:排序
    */
   public Integer getOrderNum() {
      return orderNum;
   }

   /**
    * 设置:创建时间
    */
   public void setGmtCreate(Date gmtCreate) {
      this.gmtCreate = gmtCreate;
   }

   /**
    * 获取:创建时间
    */
   public Date getGmtCreate() {
      return gmtCreate;
   }

   /**
    * 设置:修改时间
    */
   public void setGmtModified(Date gmtModified) {
      this.gmtModified = gmtModified;
   }

   /**
    * 获取:修改时间
    */
   public Date getGmtModified() {
      return gmtModified;
   }

   @Override
   public String toString() {
      return "MenuDO{" +
            "menuId=" + menuId +
            ", parentId=" + parentId +
            ", name='" + name + '\'' +
            ", url='" + url + '\'' +
            ", perms='" + perms + '\'' +
            ", type=" + type +
            ", icon='" + icon + '\'' +
            ", orderNum=" + orderNum +
            ", gmtCreate=" + gmtCreate +
            ", gmtModified=" + gmtModified +
            '}';
   }
}


通用的tree

package com.javaxl.bootdo.common.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSON;

/**
 * tree TODO <br>
 * 
 * @author kangxu2 2017-1-7
 * 
 */
public class Tree<T> {
   /**
    * 节点ID
    */
   private String id;
   /**
    * 显示节点文本
    */
   private String text;
   /**
    * 节点状态,open closed
    */
   private Map<String, Object> state;
   /**
    * 节点是否被选中 true false
    */
   private boolean checked = false;
   /**
    * 节点属性
    */
   private Map<String, Object> attributes;

   /**
    * 节点的子节点
    */
   private List<Tree<T>> children = new ArrayList<Tree<T>>();

   /**
    * 父ID
    */
   private String parentId;
   /**
    * 是否有父节点
    */
   private boolean hasParent = false;
   /**
    * 是否有子节点
    */
   private boolean hasChildren = false;

   public String getId() {
      return id;
   }

   public void setId(String id) {
      this.id = id;
   }

   public String getText() {
      return text;
   }

   public void setText(String text) {
      this.text = text;
   }

   public Map<String, Object> getState() {
      return state;
   }

   public void setState(Map<String, Object> state) {
      this.state = state;
   }

   public boolean isChecked() {
      return checked;
   }

   public void setChecked(boolean checked) {
      this.checked = checked;
   }

   public Map<String, Object> getAttributes() {
      return attributes;
   }

   public void setAttributes(Map<String, Object> attributes) {
      this.attributes = attributes;
   }

   public List<Tree<T>> getChildren() {
      return children;
   }

   public void setChildren(List<Tree<T>> children) {
      this.children = children;
   }

   public boolean isHasParent() {
      return hasParent;
   }

   public void setHasParent(boolean isParent) {
      this.hasParent = isParent;
   }

   public boolean isHasChildren() {
      return hasChildren;
   }

   public void setChildren(boolean isChildren) {
      this.hasChildren = isChildren;
   }

   public String getParentId() {
      return parentId;
   }

   public void setParentId(String parentId) {
      this.parentId = parentId;
   }

   public Tree(String id, String text, Map<String, Object> state, boolean checked, Map<String, Object> attributes,
         List<Tree<T>> children, boolean isParent, boolean isChildren, String parentID) {
      super();
      this.id = id;
      this.text = text;
      this.state = state;
      this.checked = checked;
      this.attributes = attributes;
      this.children = children;
      this.hasParent = isParent;
      this.hasChildren = isChildren;
      this.parentId = parentID;
   }

   public Tree() {
      super();
   }

   @Override
   public String toString() {

      return JSON.toJSONString(this);
   }

}


Sql语句


<select id="listMenuByUserId" resultType="com.javaxl.bootdo.system.domain.MenuDO">
   select distinct
   m.menu_id , parent_id, name, url,
   perms,`type`,icon,order_num,gmt_create, gmt_modified
   from sys_menu m
   left
   join sys_role_menu rm on m.menu_id = rm.menu_id left join
   sys_user_role ur
   on rm.role_id =ur.role_id where ur.user_id = #{id}
   and
   m.type in(0,1)
   order by
   m.order_num
</select>


Service层代码


public List<Tree<MenuDO>> listMenuTree(Long id) {
      List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();
//    这个是查询出所有数据,但是并没有形成树形结构
      List<MenuDO> menuDOs = menuMapper.listMenuByUserId(id);
      for (MenuDO sysMenuDO : menuDOs) {
         Tree<MenuDO> tree = new Tree<MenuDO>();
         tree.setId(sysMenuDO.getMenuId().toString());
         tree.setParentId(sysMenuDO.getParentId().toString());
         tree.setText(sysMenuDO.getName());
         Map<String, Object> attributes = new HashMap<>(16);
         attributes.put("url", sysMenuDO.getUrl());
         attributes.put("icon", sysMenuDO.getIcon());
         tree.setAttributes(attributes);
         trees.add(tree);
      }
      // 默认顶级菜单为0,根据数据库实际情况调整
      List<Tree<MenuDO>> list = BuildTree.buildList(trees, "0");
      return list;
   }


工具类构建树

public static <T> List<Tree<T>> buildList(List<Tree<T>> nodes, String idParam) {
      if (nodes == null) {
         return null;
      }
      List<Tree<T>> topNodes = new ArrayList<Tree<T>>();

//    下面整段代码的意思是,children要不就是顶级节点的儿子,要不就是其他分支节点的儿子(子节点)
      for (Tree<T> children : nodes) {
         String pid = children.getParentId();
//       拿到父ID,如果是顶级节点,那么放入topNodes中,结束当前循环;
         if (pid == null || idParam.equals(pid)) {
            topNodes.add(children);
            continue;
         }

//       父ID不是顶级节点,接着下面逻辑处理
         for (Tree<T> parent : nodes) {
            String id = parent.getId();
            if (id != null && id.equals(pid)) {
               parent.getChildren().add(children);
               children.setHasParent(true);
               parent.setChildren(true);
               continue;
            }
         }

      }
      return topNodes;
   }


Controler层代码


@Log("请求访问主页")
@GetMapping({"/index"})
String index(Model model) {
    List<Tree<MenuDO>> menus = menuService.listMenuTree(getUserId());
    model.addAttribute("menus", menus);
    model.addAttribute("name", getUser().getName());
    FileDO fileDO = fileService.get(getUser().getPicId());
    if (fileDO != null && fileDO.getUrl() != null) {
        if (fileService.isExist(fileDO.getUrl())) {
            model.addAttribute("picUrl", fileDO.getUrl());
        } else {
            model.addAttribute("picUrl", "/img/photo_s.jpg");
        }
    } else {
        model.addAttribute("picUrl", "/img/photo_s.jpg");
    }
    model.addAttribute("username", getUser().getUsername());
    return "index_v1";
}


前端html代码

<li th:each="menu : ${menus}"><a href="#"> <i
        class="fa fa fa-bar-chart-o" th:class="${menu.attributes.icon}"></i>
    <span class="nav-label" th:text="${menu.text}">基础信息</span> <span
            class="fa arrow"></span>
</a>
    <ul class="nav nav-second-level">
        <li th:each="cmenu : ${menu.children}"><a class="J_menuItem" href="graph_echarts.html"
                                                  th:text="${cmenu.text}"
                                                  th:href="${cmenu.attributes.url}">系统管理</a></li>
    </ul>
</li>


系统菜单功能

Controler

@RequiresPermissions("sys:menu:menu")
@RequestMapping("/list")
@ResponseBody
List<MenuDO> list(@RequestParam Map<String, Object> params) {
   List<MenuDO> menus = menuService.list(params);
   return menus;
}


Service

public List<MenuDO> list(Map<String, Object> params) {
   List<MenuDO> menus = menuMapper.list(params);
   return menus;
}


Sql语句

<select id="list" resultType="com.javaxl.bootdo.system.domain.MenuDO">
   select
   `menu_id`,`parent_id`,`name`,`url`,`perms`,`type`,`icon`,`order_num`,`gmt_create`,`gmt_modified`
   from sys_menu
   <where>
      <if test="menuId != null and menuId != ''"> and menu_id = #{menuId} </if>
      <if test="parentId != null and parentId != ''"> and parent_id = #{parentId} </if>
      <if test="name != null and name != ''"> and name = #{name} </if>
      <if test="url != null and url != ''"> and url = #{url} </if>
      <if test="perms != null and perms != ''"> and perms = #{perms} </if>
      <if test="type != null and type != ''"> and type = #{type} </if>
      <if test="icon != null and icon != ''"> and icon = #{icon} </if>
      <if test="orderNum != null and orderNum != ''"> and order_num = #{orderNum} </if>
      <if test="gmtCreate != null and gmtCreate != ''"> and gmt_create = #{gmtCreate} </if>
      <if test="gmtModified != null and gmtModified != ''"> and gmt_modified = #{gmtModified} </if>
   </where>
   <choose>
      <when test="sort != null and sort.trim() != ''">
         order by ${sort} ${order}
      </when>
      <otherwise>
         order by menu_id desc
      </otherwise>
   </choose>
   <if test="offset != null and limit != null">
      limit #{offset}, #{limit}
   </if>
</select>


前端代码

menu.html 

menu.js 


角色管理功能



function getMenuTreeData() {
   var roleId = $('#roleId').val();
   $.ajax({
      type : "GET",
      url : "/sys/menu/tree/" + roleId,
      success : function(data) {
         loadMenuTree(data);
      }
   });
}


@GetMapping("/tree/{roleId}")
@ResponseBody
Tree<MenuDO> tree(@PathVariable("roleId") Long roleId) {
   Tree<MenuDO> tree = menuService.getTree(roleId);
   return tree;
}


@Override
public Tree<MenuDO> getTree(Long id) {
   // 根据roleId查询权限
   List<MenuDO> menus = menuMapper.list(new HashMap<String, Object>(16));
   List<Long> menuIds = roleMenuMapper.listMenuIdByRoleId(id);
   List<Long> temp = menuIds;
   for (MenuDO menu : menus) {
      if (temp.contains(menu.getParentId())) {
         menuIds.remove(menu.getParentId());
      }
   }
   List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();
   List<MenuDO> menuDOs = menuMapper.list(new HashMap<String, Object>(16));
   for (MenuDO sysMenuDO : menuDOs) {
      Tree<MenuDO> tree = new Tree<MenuDO>();
      tree.setId(sysMenuDO.getMenuId().toString());
      tree.setParentId(sysMenuDO.getParentId().toString());
      tree.setText(sysMenuDO.getName());
      Map<String, Object> state = new HashMap<>(16);
      Long menuId = sysMenuDO.getMenuId();
      if (menuIds.contains(menuId)) {
         state.put("selected", true);
      } else {
         state.put("selected", false);
      }
      tree.setState(state);
      trees.add(tree);
   }
   // 默认顶级菜单为0,根据数据库实际情况调整
   Tree<MenuDO> t = BuildTree.build(trees);
   return t;
}


角色修改界面

edit.html 


系统用户功能

选择部门的树形加载代码

实体类

public class DeptDO implements Serializable {
   private static final long serialVersionUID = 1L;
   
   //
   private Long deptId;
   //上级部门ID,一级部门为0
   private Long parentId;
   //部门名称
   private String name;
   //排序
   private Integer orderNum;
   //是否删除  -1:已删除  0:正常
   private Integer delFlag;


Service

public Tree<DeptDO> getTree() {
    List<Tree<DeptDO>> trees = new ArrayList<Tree<DeptDO>>();
    List<DeptDO> sysDepts = sysDeptMapper.list(new HashMap<String, Object>(16));
    for (DeptDO sysDept : sysDepts) {
        Tree<DeptDO> tree = new Tree<DeptDO>();
        tree.setId(sysDept.getDeptId().toString());
        tree.setParentId(sysDept.getParentId().toString());
        tree.setText(sysDept.getName());
        Map<String, Object> state = new HashMap<>(16);
        state.put("opened", true);
        tree.setState(state);
        trees.add(tree);
    }
    // 默认顶级菜单为0,根据数据库实际情况调整
    Tree<DeptDO> t = BuildTree.build(trees);
    return t;
}


用户管理没啥难度,这里就省略了......

user.html 

user.js 





关键字:     解决方案       权限  

备案号:湘ICP备19000029号

Copyright © 2018-2019 javaxl晓码阁 版权所有