上一篇博客我带大家实现了EasyUI的树形菜单。
下面我带大家使用上面的知识来实现一个简单的权限管理
权限管理概念
权限管理的目的是为了让不同的用户可以操作系统中不同资源。比如上面的菜单带比方,不同的用户它能看的菜单项的数量是不同的。能操作的功能模块也是不同的。权限管理的实现方式也是不同的,更具你的具体业务场景来做选择。
权限关的数据库设计(三种设计方案)
第一种
权限管理最主要的就是数据库的设计,根据数据库的表与表之间的关联来实现权限。下面介绍三种权限实现的数据库设计
第一种实现方式通过用户与菜单表的关联来实现权限控制,中间添加了一个用户权限关联表就是实现用户表和权限表的权限所在。当然这种设置也存在一个问题。比如说,所有员工的权限都是一样的,那么我们的权限系统要录了100人的信息的时候那么就会非常通过,因为每一个就需要配对相应的权限菜单。所有下面这种权限表设计就引入了角色的概念。
第二种
第二种比第一种复杂在引入角色的概念,角色对应多个菜单权限集。用户和角色关联,通过角色来获取它所拥有的权限集合。来实现权限关联,不过上面的有个缺点就是一个用户只能对应一个角色。如果要用户角色多对对那么就才用第三种吧
第三种
加一张简单的用户角色关联表就可以实现用户角色的多对多啦。功能也要比上面定义的更强大
权限关联的实现方式还用很多复制的实现。比如更厉害的按钮级权限,可以实现控制到页面按钮级别的权限关联。实现的难度系数当然也更大,这里就不过的介绍,感兴趣的可以问度娘。
下面我来讲讲第一种表设置的实现,其他两种就不讲啦
代码实现
**大体思路:**先从登录开始,用户登录后去后台读通过用户的id读取权限关联表对应的菜单权限,将对应的菜单项读取出来后展示即可。
后端代码
Dao类
MenuDao
该类根据用户所拥有的权限,去获取权限菜单
/**
* 入库方法
*
* 返回用户拥有的所有权限节点
* @param map request.getParameterMap 参数对象
* @param pageBean 分页对象,这里不管他,树形菜单不分页,使用该值为null
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws SQLException
*/
public List<TreeNode> list(Map<String, String[]> map, PageBean pageBean)
throws InstantiationException, IllegalAccessException, SQLException {
//根据前台参数获取对应权限菜单
List<Map<String, Object>> listMenu = this.listMenuSef(map, pageBean);
//定义TreeNode阶段集合,使用TreeNode的原因是因为将TreeNode转成JSON格式符合EasyUI的规范
List<TreeNode> treeNodeList = new ArrayList<>();
//将listMenu集合中的Map<Stirng, Object>节点转为treeNodeList集合的TreeNode节点
//使用到了递归的思想,使用递归将每个节点的子节点查出来,将权限表的节点的子权限展示出来
menu2TreeNodeList(listMenu, treeNodeList);
return treeNodeList;
}
/**
* 专门给第一次获取权限节点定义的方法,为了防止无限递归。
* @param map
* @param pageBean
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws SQLException
*/
public List<Map<String, Object>> listMenuSef(Map<String, String[]> map, PageBean pageBean)
throws InstantiationException, IllegalAccessException, SQLException {
String sql = "select * from t_easyui_menu where true ";
//获取拥有的权限菜单 id集合 以 1,2,3 的格展示
String id = JsonUtils.getParamVal(map, "menuHid");
//如果id不等于null或去空格后不等于"",则返回true,否则返回false
if (StringUtils.isNotBlank(id)) {
sql = sql + " and menuid in (" + id + ") ";
}
//用户所拥有的菜单权限,这里注意并每有使用该方法进行递归,主要用于实现第一次查询节点,
//listMenu 方法主要是查询该方法查询节点的子节点
return super.executeQuery(sql, pageBean);
}
/**
* 这是查询指定父节点下的子节点
*
* @param map 主要是用来传递参数的
* @param pageBean
* @return
* @throws SQLException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public List<Map<String, Object>> listMenu(Map<String, String[]> map, PageBean pageBean)
throws InstantiationException, IllegalAccessException, SQLException {
String sql = "select * from t_easyui_menu where parentid = ";
// 该方法的作用是获取key为id的value数组值,如果有单个就显示单个值,如果有多个那么就显示拼接值,这里一般为单个
String id = JsonUtils.getParamVal(map, "id");
//如果id不等于null或去空格后不等于"",则返回true,否则返回false
if (StringUtils.isNotBlank(id)) {
sql = sql + id + " ";
} else {
// 未指定就使用默认的查询方式,即父节点为 -1的
sql = sql + "-1 ";
}
/**
* 这是去数据库查询这张表,Map集合的key记录列段名 value记录列段值,每一个Map代表一行,List就是查询出来的结果集合
*/
return super.executeQuery(sql, pageBean);
}
/**
* [{Menuid:1,.....[]},{Menuid:2,.....[]}] ->[{id:1,.....[]},{id:2,.....[]}]
* menu表的数据不符合easyui树形方式 需要转换成easyui能识别的格式。也就是TrreNode实体类
* Map -> TreeNode 因为将TreeNode转为JSON格式符合easyui的规范
* @param map 这是要转换的那那一个节点
* @param treeNode
* @throws SQLException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void menu2TreeNode(Map<String, Object> map, TreeNode treeNode)
throws InstantiationException, IllegalAccessException, SQLException {
//节点id树形设置
treeNode.setId(map.get("Menuid").toString());
//节点text(文本)树形设置
treeNode.setText(map.get("Menuname").toString());
//节点属性就将查询出来的那一行扔进去啦
treeNode.setAttributes(map);
Map<String, String[]> jspMap = new HashMap<String, String[]>();
// 这是指定父节点的id
jspMap.put("id", new String[] { treeNode.getId() });
// 获取该节点下面的子节点
List<Map<String, Object>> listMenu = this.listMenu(jspMap, null);
// 定义子节点存储集合
List<TreeNode> treeNodeList = new ArrayList<>();
// 这是将List<Map<String, Object>> 存储该为 List<TreeNode>格式 是为了符合easyui的定义
//这里使用了递归的实现,多看一下就能懂
menu2TreeNodeList(listMenu, treeNodeList);
treeNode.setChildren(treeNodeList);
}
/**
* 该方法的作用是将 List<Map<String, Object>> -> List<TreeNode> 格式主要的作用是为JSON格式而准备
* @param mapList
* @param treeNodeList
* @throws InstantiationException
* @throws IllegalAccessException
* @throws SQLException
*/
private void menu2TreeNodeList(List<Map<String, Object>> mapList, List<TreeNode> treeNodeList)
throws InstantiationException, IllegalAccessException, SQLException {
TreeNode treeNode = null;
for (Map<String, Object> map : mapList) {
treeNode = new TreeNode();
menu2TreeNode(map, treeNode);
treeNodeList.add(treeNode);
}
}
UserDao
用户登录,和获取该用户所拥有的权限的功能
public class UserDao extends JsonBaseDao {
/**
* 用户登陆的方法
*
* @param paMap
* @param pageBean
* @return
* @throws SQLException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public List<Map<String, Object>> list(Map<String, String[]> paMap, PageBean pageBean) throws InstantiationException, IllegalAccessException, SQLException {
//
String sql = "select * from t_easyui_user_version2 where true ";
//用户名密码
String uid = JsonUtils.getParamVal(paMap, "uid");
String upwd = JsonUtils.getParamVal(paMap, "upwd");
// 如果字符串不等于null或去空格后不等于"",则返回true,否则返回false
if(StringUtils.isNotBlank(uid)) {
sql = sql + " and uid = " + uid;
}
if(StringUtils.isNotBlank(upwd)) {
sql = sql + " and upwd = " + upwd;
}
//执行方法,返回数据库查询结构。调用方法通过判断是否就结构就知道数据库中是否有该用户
return super.executeQuery(sql, pageBean);
}
/**
* 通过中间表查询用户所对应的权限
* @param paMap
* @param pageBean
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws SQLException
*/
public List<Map<String, Object>> listMenu(String uid, PageBean pageBean) throws InstantiationException, IllegalAccessException, SQLException {
//t_easyui_usermenu 是中间表
String sql = "select * from t_easyui_usermenu where true ";
//根具用户id进行查询
if(StringUtils.isNotBlank(uid)) {
sql = sql + " and uid = " + uid;
}
return super.executeQuery(sql, pageBean);
}
}
请求处理
UserActioin
该类的作用是用户登录验证如果验证成功会将数据转发前端
/**
* 用户登陆请求类
* @author 20190313
*
*/
public class UserAction extends ActionSupport {
//这是封装了内部的userDao类
private UserDao userDao = new UserDao();
/**
* 用户登录的方法
* @param req
* @param response
* @return
*/
public String login(HttpServletRequest req, HttpServletResponse response) {
try {
//先登陆看看是否可以登录成功
List<Map<String, Object>> list = this.userDao.list(req.getParameterMap(), null);
//判断是否登录成功 登录
if(list != null && list.size() > 0) {
//这是获取用户的权限
List<Map<String, Object>> listMenu = this.userDao.listMenu (req.getParameter("uid"), null);
//这是用来拼接权限的
StringBuilder sb = new StringBuilder();
for (Map<String, Object> map : listMenu) {
//这是将用户拥有的菜单权并将起来
sb.append("," + map.get("menuId"));
}
//,001,002 (将第一个开头的 ”,“去掉) 使用转发来传到前端页面
req.setAttribute("menuHid", sb.substring(1));
}else {
return "login";
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return "index";
}
}
MenuAction
该方法用于获取用户所拥有的权限菜单
public class MenuAction extends ActionSupport {
private MenuDao menuDao = new MenuDao();
public String treeMenu(HttpServletRequest req, HttpServletResponse response) throws Exception {
//获取用户的菜单权限菜单集合(根据前台的参数)
List<TreeNode> list = this.menuDao.list(req.getParameterMap(), null);
//拥有JSON格式输出
ObjectMapper om = new ObjectMapper();
//格式化后的JSON格式
String jsonStr = om.writeValueAsString(list);
System.out.println(jsonStr);
// 将list集合转换成json串
ResponseUtil.write(response, jsonStr);
return "index";
}
}
前台代码
登录
登录会验证用户是否登录成功,登录成功获取该用户拥有的权限
<body>
<form action="${pageContext.request.contextPath }/userAction.action?methodName=login" method="post">
uid:<input type="text" name="uid"><br>
upwd:<input type="password" name="upwd"><br>
<input type="submit" value="登陆">
</form>
</body>
首页index
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 注意:JS有引顺序 -->
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath }/static/js/themes/default/easyui.css">
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath }/static/js/themes/icon.css">
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/jquery.min.js"></script>
<script type="text/javascript"
src="${pageContext.request.contextPath }/static/js/jquery.easyui.min.js"></script>
<script src="${pageContext.request.contextPath }/static/index.js"></script>
<title>Insert title here</title>
</head>
<body class="easyui-layout">
<!--在是关键代码,当用户登录成功后这里保存了用户所拥有的权限id-->
<input type="hidden" id="menuHid" value="${menuHid}">
<div data-options="region:'north',border:false"
style="height: 60px; background: #B3DFDA; padding: 10px">north
region</div>
<div data-options="region:'west',split:true,title:'West'"
style="width: 150px; padding: 10px;">
后台管理界面的菜单
<ul id="tt"></ul>
</div>
<div
data-options="region:'east',split:true,collapsed:true,title:'East'"
style="width: 100px; padding: 10px;">east region</div>
<div data-options="region:'south',border:false"
style="height: 50px; background: #A9FACD; padding: 10px;">south
region</div>
<div data-options="region:'center',title:'Center'">
</div>
</body>
</html>
上面代码中这关键。其中保存了用户所拥有的权限,js代码通过读取value值向后端发ajax请求。将用户所拥有的权限菜单请求来
<input type="hidden" id="menuHid" value="${menuHid}">
index.js
$(function() {
$('#tt').tree({
//向后台请求该用户的树形菜单 menuHid=' + $("#menuHid").val() 是带着该用户所拥有的权限集过去的
url : 'menuAction.action?methodName=treeMenu&&menuHid=' + $("#menuHid").val(),
//点击tabs页触发的事件
onClick : function(node) {
//使用框架来连接页面
var content = '<iframe scrolling="on" frameborder="0" src="'+node.attributes.menuURL+'" width="99%" height="99%"></iframe>'
//判断是否以及创建
if ($("#menuTabs").tabs('exists', node.text)) {
//创建了就改为选中·
$("#menuTabs").tabs('select', node.text);
} else {
//每又创建就新增
$('#menuTabs').tabs('add', {
title : node.text,
content : content,
closable : true,
tools : [ {
iconCls : 'icon-mini-refresh',
handler : function() {
alert('refresh');
}
} ]
});
}
}
});
})
就这样实现了权限关联的基本构建 拜