JavaEE权限管理系统的搭建(五)--------RBAC权限管理中的权限菜单的显示
上一小节实现了登录的实现,本小节实现登录后根据用户名查询当前用户的角色所关联的所有权限,然后进行菜单的显示。登录成功后,如下图所示,管理设置是一级菜单,管理员列表,角色管理,权限管理是二级菜单。
先来看一下,AdminUser类,Role类,Permission类
AdminUser类
package com.supin51.domain; import org.apache.ibatis.type.Alias; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.io.Serializable; import java.util.List; /** * @Author:ShaoJiang * @description: * @Date: created in 上午10:05 2019/1/21 * @Modified By: */ @Alias("adminUser") public class AdminUser implements Serializable { private Integer id; @NotBlank(message = "用户名不能为空") @Length(min = 4,max = 12,message = "用户名格式必须在4-12位字符之间") private String LoginName; @NotBlank(message = "密码不能为空") @Size(min = 6,max = 12,message = "密码必须在6-12位任意字符之间") private String password; private Integer state; //用户角色集合,多对多关系,一个用户=多个角色,该属性显示用户的角色名称 private String userRoles; public AdminUser() {super();} public AdminUser(Integer id, String loginName, String password, Integer state) { this.id = id; LoginName = loginName; this.password = password; this.state = state; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLoginName() { return LoginName; } public void setLoginName(String loginName) { LoginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getState() { return state; } public void setState(Integer state) { this.state = state; } public String getUserRoles() { return userRoles; } public void setUserRoles(String userRoles) { this.userRoles = userRoles; } }
Role类
package com.supin51.domain; import org.apache.ibatis.type.Alias; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; import java.io.Serializable; /** * @Author:ShaoJiang * @description: * @Date: created in 下午2:08 2019/1/24 * @Modified By: */ @Alias("role") public class Role implements Serializable { private Integer id; @NotBlank(message = "角色名不能为空") @Length(max = 16,message = "角色名字符数过长") private String roleName; private String roleRemark; public Role() { } public Role(Integer id, String roleName, String roleRemark) { this.id = id; this.roleName = roleName; this.roleRemark = roleRemark; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleRemark() { return roleRemark; } public void setRoleRemark(String roleRemark) { this.roleRemark = roleRemark; } }
Permission类:
package com.supin51.domain; import org.apache.ibatis.type.Alias; import java.io.Serializable; import java.util.List; /** * @Author:ShaoJiang * @description: * @Date: created in 上午10:47 2019/1/22 * @Modified By: */ @Alias("permission") public class Permission implements Serializable { private Integer id;//权限编号 private Integer parentId;//父节点编号 private String name;//权限名称 private String code;//权限代码 private String url;//权限URL private String icon;//权限图标样式 private String category;//权限类别 private String remark;//权限备注 //子权限集合 List<Permission> childPermissions; public Permission() { } public Permission(Integer id, Integer parentId, String name, String code, String url, String icon, String category, String remark) { this.id = id; this.parentId = parentId; this.name = name; this.code = code; this.url = url; this.icon = icon; this.category = category; this.remark = remark; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getParentId() { return parentId; } public void setParentId(Integer parentId) { this.parentId = parentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public List<Permission> getChildPermissions() { return childPermissions; } public void setChildPermissions(List<Permission> childPermissions) { this.childPermissions = childPermissions; } @Override public String toString() { return "Permission{" + "id=" + id + ", parentId=" + parentId + ", name='" + name + '\'' + ", code='" + code + '\'' + ", url='" + url + '\'' + ", icon='" + icon + '\'' + ", category='" + category + '\'' + ", remark='" + remark + '\'' + ", childPermissions=" + childPermissions + '}'; } }
数据库t_permission表的结构
t_role表
t_admin表:password储存的全是加密过的密码
t_amdin_role表:用户和角色的关系表(中间表)
t_role_permission表:角色和权限的关系表(中间表)
可以看到,用户admin有2个角色:管理员和aaa,管理员拥有所有权限,aaa也拥有了所有权限
登录成功后跳转到主页后
AdminController中的代码
/** * 加载主页 * @return */ @RequestMapping("/main") public String main(HttpSession session,Model model) { if(session.getAttribute("adminUser")!=null){ logger.info("adminUser:"+session.getAttribute("adminUser").toString()); AdminUser adminUser = (AdminUser)session.getAttribute("adminUser"); //查找登录用户角色下的所有权限 List<Permission> allPermissionsList = adminUserService.findAdminUserRolePermissionList(adminUser.getLoginName().toString());
//如果没有权限,直接返回主页 if(allPermissionsList.size()<=0) { return "main"; } //遍历所有节点,先查找出根节点集合 List<Permission> permissionList = new ArrayList<Permission>(); for(Permission p:allPermissionsList){ //parentId为0的代表根节点 if(p.getParentId().toString().equals("0") ||p.getParentId()==0){ permissionList.add(p); } } //外层循环根节点集合 for(Permission p:permissionList){ //声明一个集合用于装载子节点 List<Permission> childPermissions = new ArrayList<Permission>(); //内层遍历所有权限列表 for(Permission p2:allPermissionsList){ //如果节点的parentId等于根节点父节点的ID if(p.getId().equals(p2.getParentId())){ //添加节点到子节点集合 childPermissions.add(p2); } } //设置根节点下的子节点集合 p.setChildPermissions(childPermissions); } logger.info(Arrays.asList(permissionList).toString()); //加载到requestScope内,在页面进行获取 model.addAttribute("permissionList",permissionList); } return "main"; }
当用户拥有两个角色,角色中拥有相同的权限时,怎么获取和显示呢?
这里主要讲解一下,获取用户所有角色下的所有权限的持久层方法
AdminUserMapper.xml ----对应AdminUserDao接口类
<!--查找登录用户下所有角色权限信息--> <select id="findAdminUserRolePermissionList" parameterType="string" resultType="com.supin51.domain.Permission"> SELECT DISTINCT tp.* FROM t_admin ta LEFT JOIN t_admin_role tar ON tar.adminId = ta.id LEFT JOIN t_role tr ON tar.roleId = tr.id LEFT JOIN t_role_permission trp ON trp.roleId = tr.id LEFT JOIN t_permission tp ON tp.id = trp.permissionId WHERE ta.loginName = #{loginName} AND tp.parentId IS NOT NULL </select>
这个是多表连接查询,其中使用了关键字:DISTINCT(去重),因为一个用户可以有多个角色,一个角色可以有多个权限,当用户同时拥有2个角色或以上时,而两个角色中都拥有相同的权限时,必须要使用这个关键字DISTINCT(去重),否则相同的权限也会一并查询出来显示。
main.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%-- Created by IntelliJ IDEA. User: shaojiang Date: 2019/1/20 Time: 下午7:07 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name="renderer" content="webkit|ie-comp|ie-stand"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" /> <meta http-equiv="Cache-Control" content="no-siteapp" /> <link rel="Bookmark" href="/favicon.ico" > <link rel="Shortcut Icon" href="/favicon.ico" /> <!--[if lt IE 9]> <script type="text/javascript" src="/statics/lib/html5shiv.js"></script> <script type="text/javascript" src="/statics/lib/respond.min.js"></script> <![endif]--> <link rel="stylesheet" type="text/css" href="/statics/h-ui/css/H-ui.min.css" /> <link rel="stylesheet" type="text/css" href="/statics/h-ui.admin/css/H-ui.admin.css" /> <link rel="stylesheet" type="text/css" href="/statics/lib/Hui-iconfont/1.0.8/iconfont.css" /> <link rel="stylesheet" type="text/css" href="/statics/h-ui.admin/skin/default/skin.css" id="skin" /> <link rel="stylesheet" type="text/css" href="/statics/h-ui.admin/css/style.css" /> <!--[if IE 6]> <script type="text/javascript" src="/statics/lib/DD_belatedPNG_0.0.8a-min.js" ></script> <script>DD_belatedPNG.fix('*');</script> <![endif]--> <title>H-ui.admin v3.1</title> </head> <body> <header class="navbar-wrapper"> <div class="navbar navbar-fixed-top"> <div class="container-fluid cl"> <a class="logo navbar-logo f-l mr-10 hidden-xs" href="/aboutHui.shtml">H-ui.admin</a> <a class="logo navbar-logo-m f-l mr-10 visible-xs" href="/aboutHui.shtml">H-ui</a> <span class="logo navbar-slogan f-l mr-10 hidden-xs">v3.1</span> <a aria-hidden="false" class="nav-toggle Hui-iconfont visible-xs" href="javascript:;"></a> <nav id="Hui-userbar" class="nav navbar-nav navbar-userbar hidden-xs"> <ul class="cl"> <li></li> <li class="dropDown dropDown_hover"> <a href="#" class="dropDown_A">当前用户:${sessionScope.adminUser.loginName}<i class="Hui-iconfont"></i></a> <ul class="dropDown-menu menu radius box-shadow"> <li><a href="javascript:;" onclick="modifyPwd()" >修改密码</a></li> <li><a href="javascript:;" onclick="logout()" >退出</a></li> </ul> </li> <li id="Hui-msg"> <a href="#" title="消息"><span class="badge badge-danger">1</span><i class="Hui-iconfont" style="font-size:18px"></i></a> </li> <li id="Hui-skin" class="dropDown right dropDown_hover"> <a href="javascript:;" class="dropDown_A" title="换肤"><i class="Hui-iconfont" style="font-size:18px"></i></a> <ul class="dropDown-menu menu radius box-shadow"> <li><a href="javascript:;" data-val="default" title="默认(黑色)">默认(黑色)</a></li> <li><a href="javascript:;" data-val="blue" title="蓝色">蓝色</a></li> <li><a href="javascript:;" data-val="green" title="绿色">绿色</a></li> <li><a href="javascript:;" data-val="red" title="红色">红色</a></li> <li><a href="javascript:;" data-val="yellow" title="黄色">黄色</a></li> <li><a href="javascript:;" data-val="orange" title="橙色">橙色</a></li> </ul> </li> </ul> </nav> </div> </div> </header> <aside class="Hui-aside"> <div class="menu_dropdown bk_2"> <%--这里控制权限菜单的显示--%> <c:forEach var="item" items="${permissionList}" begin="0"> <dl> <dt><i class="${item.icon}"></i> ${item.name}<i class="Hui-iconfont menu_dropdown-arrow"></i></dt> <dd> <ul> <c:forEach var="item_subitem" items="${item.childPermissions}"> <li><a data-href="${item_subitem.url}" data-title="${item_subitem.name}" href="javascript:void(0)">${item_subitem.name}</a></li> </c:forEach> </ul> </dd> </dl> </c:forEach> </div> </aside> <div class="dislpayArrow hidden-xs"><a class="pngfix" href="javascript:void(0);" onClick="displaynavbar(this)"></a></div> <section class="Hui-article-box"> <div id="Hui-tabNav" class="Hui-tabNav hidden-xs"> <div class="Hui-tabNav-wp"> <ul id="min_title_list" class="acrossTab cl"> <li class="active"> <span title="我的桌面" data-href="welcome.html">我的桌面</span> <em></em></li> </ul> </div> <div class="Hui-tabNav-more btn-group"><a id="js-tabNav-prev" class="btn radius btn-default size-S" href="javascript:;"><i class="Hui-iconfont"></i></a><a id="js-tabNav-next" class="btn radius btn-default size-S" href="javascript:;"><i class="Hui-iconfont"></i></a></div> </div> <div id="iframe_box" class="Hui-article"> <div class="show_iframe"> <div style="display:none" class="loading"></div> <iframe scrolling="yes" frameborder="0" src="/admin/welcome"></iframe> </div> </div> </section> <div class="contextMenu" id="Huiadminmenu"> <ul> <li id="closethis">关闭当前 </li> <li id="closeall">关闭全部 </li> </ul> </div> <!--_footer 作为公共模版分离出去--> <script type="text/javascript" src="/statics/lib/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="/statics/lib/layer/2.4/layer.js"></script> <script type="text/javascript" src="/statics/h-ui/js/H-ui.min.js"></script> <script type="text/javascript" src="/statics/h-ui.admin/js/H-ui.admin.js"></script> <!--/_footer 作为公共模版分离出去--> <!--请在下方写此页面业务相关的脚本--> <script type="text/javascript" src="/statics/lib/jquery.contextmenu/jquery.contextmenu.r2.js"></script> <script type="text/javascript"> $(function(){ /*$("#min_title_list li").contextMenu('Huiadminmenu', { bindings: { 'closethis': function(t) { console.log(t); if(t.find("i")){ t.find("i").trigger("click"); } }, 'closeall': function(t) { alert('Trigger was '+t.id+'\nAction was Email'); }, } });*/ }); /*修改密码*/ function modifyPwd(){ parent.layer.open({ title:'修改登录密码', maxmin: true, type: 2, shadeClose: false, //点击遮罩关闭 shade: 0.5,//阴影 content: '/admin/update-login-password.action', area: ['500px', '300px'] }); } /*退出系统*/ function logout() { layer.open({ title: '注销登录?', content: '确定要退出该系统?' ,btn: ['确定', '取消'] ,yes: function(index, layero){ //按钮【按钮一】的回调 layer.close(index); window.location.href = "/admin/logout"; },btn2: function(index, layero){ //按钮【按钮二】的回调 },btn3: function(index, layero){ //按钮【按钮三】的回调 } ,cancel: function(){ //右上角关闭回调 } }); } </script> </body> </html>
logout方法
/** * 退出系统 * @return */ @RequestMapping("/logout") public String logout(HttpServletRequest request, SessionStatus sessionState ) { Enumeration<String> em = request.getSession().getAttributeNames(); while (em.hasMoreElements()) { request.getSession().removeAttribute(em.nextElement().toString()); } request.getSession().removeAttribute("adminUser"); //注销登录,销毁session request.getSession().invalidate(); //sessionStatus中的setComplete方法可以将session中的内容全部清空 sessionState.setComplete(); //重定向到到登录页面action return "redirect:/admin/login"; }
换一个账户:tom登录,可以看到tom什么权限菜单也没有,因为tom属于未分组角色,所以登录后也看不到任何的菜单,只有默认的修改密码一个功能可用。
至此,权限菜单的动态显示完成,本小节结束。下一小节将讲解点击菜单进行权限的拦截验证放行跳转,以及控制页面的权限按钮(三级菜单)显示。