每个系统都有权限模块,权限就是针对于系统资源(菜单、按钮等等)进行统一管理;
让不同的用户能够使用同一个系统中的不同资源;
本篇博客就给大家介绍一种通用的系统权限设计方案,当然权限设计的方式有很多种,这只是其中的一种。本篇博客设计到的主要技术有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晓码阁 版权所有