每个系统都有权限模块,权限就是针对于系统资源(菜单、按钮等等)进行统一管理;
让不同的用户能够使用同一个系统中的不同资源;
本篇博客就给大家介绍一种通用的系统权限设计方案,当然权限设计的方式有很多种,这只是其中的一种。本篇博客设计到的主要技术有springboot、bootstrap、shiro等。
实体类定义
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>
前端代码
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; }
角色修改界面
选择部门的树形加载代码
实体类
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;
}
用户管理没啥难度,这里就省略了......
备案号:湘ICP备19000029号
Copyright © 2018-2019 javaxl晓码阁 版权所有