前言:权限管理(控制)是OA,多角色,管理类系统必不可少的模块。shiro是spring的全家桶中一员,如果项目很小,如课程设计,毕业设计这类无需如此大的组件的话,可以考虑基于RBAC原理,手写一个

Q:什么是权限管理?
Q:什么是RBAC思想?(Role Based Acess Control)
Q:RBAC实现有几种常见方式?从简单到复杂(完整设计)分别需要多少张表?
以上问题自行探索后,阅读本文更佳。

效果图:

java权限系统rpc java rbac权限管理设计_java权限系统rpc


实现效果:

  • 多角色即时授权管理等基本操作
  • 复选框勾选,授权/取消授权
  • 上一次授权结果显示
  • 按角色批量授权
  • 按具体用户定制(特殊、临时)授权

语言:java
IDE:Eclipse
JDK:1.7
Tomcat:7.0.81
MySQL:5.0
数据库图形化管理工具:SQLyog
前端依赖:js,jQuery,easyui
后端依赖:SSM(springMVC,spring,myBatis)


一、数据库表设计

至少需要5张表

第一种:基本5张表架构

java权限系统rpc java rbac权限管理设计_List_02


第二种:扩展一张用户-权限表

java权限系统rpc java rbac权限管理设计_RBAC_03

1、user(用户表) :用户基本信息

java权限系统rpc java rbac权限管理设计_List_04


2、role(角色表) :系统设计中包含的角色

java权限系统rpc java rbac权限管理设计_List_05


3、permission(权限表) :系统功能(权限)

java权限系统rpc java rbac权限管理设计_spring_06


4、user_role(用户-角色关联表) :用户是什么角色

java权限系统rpc java rbac权限管理设计_RBAC_07


5、role_permission(角色-权限关联表) :某类角色有什么权限

java权限系统rpc java rbac权限管理设计_List_08

6、user_permission(用户_权限关联表)(可选) : 某个用户有什么权限

java权限系统rpc java rbac权限管理设计_i++_09

二、后台设计

业务流程: 用户登录-》获取当前用户的角色-》查找该角色拥有的权限-》查找角色-权限表-》查找权限表-》获取对应权限
Entity: 实体对应数据库表即可。除此之外,因为前端使用了easyui的tree组件,其中对tree数据格式有一定要求,后端返回给前台的都是json,需要一个TreeNode类对数据进行封装。

public class TreeNode {
	
	private String id="0";
    private String text; // 树节点名称
    private String iconCls; // 前面的小图标样式
    private Boolean checked = false; // 是否勾选状态
    private Map<String, Object> attributes; // 自定义属性
    private List<TreeNode> children; // 子节点
    private String parentId;
    private String state = "open"; // 是否展开(open,closed)
    
	
	public String getParentId() {
		return parentId;
	}
	public void setParentId(String parentId) {
		this.parentId = parentId;
	}
	
	……
	
	public Map<String, Object> getAttributes() {
		return attributes;
	}
	public void setAttributes(Map<String, Object> attributes) {
		this.attributes = attributes;
	}
	public List<TreeNode> getChildren() {
		return children;
	}
	public void setChildren(List<TreeNode> children) {
		this.children = children;
	}
	……
	省略了部分getter和setter及toString,构造等函数
	……
}

controller处理: 这里仅是一个示例。即: 前台传递登录用户参数-》controller获取用户ID等主键信息-》调用DAO获取权限查询接口-》接口响应请求并返回结果-》controller获取结果,并做TreeNode封装处理-》用ResponseBody注解直接将数据以json返回给前台-》easyui的tree组件接收格式化的数据-》前台jsp渲染展示

controller代码:

/**
	 * 加载权限树byUser
	 * @param u
	 */
	@RequestMapping(value="/queryTreeByUser")
	@ResponseBody
	public List<TreeNode> queryTreeByUser(HttpServletRequest request,
	HttpSession session) {
		
		List<TreeNode> listTree = new ArrayList<TreeNode>();
		//封装为EasyUI Tree 数据格式
		//根据用户ID查询权限树
		List<Permission> listPermission = 
		userService.queryTreeByUser((User)session.getAttribute("userLoginInfo"));
		
		for(Permission item:listPermission) {
			TreeNode treeNode = new TreeNode(); 
			treeNode.setParentId(String.valueOf(item.getParentId()));
			treeNode.setId(String.valueOf(item.getPermission_id()));
			treeNode.setText(item.getPermission_name());
			Map map = new HashMap();
			map.put("url",item.getPermission_url());
			treeNode.setAttributes(map);
			treeNode.setState(item.getState());
			listTree.add(treeNode);
		}
		return listTree;
	}

service调用:

* 查询树byUser
	 */
	public List<Permission> queryTreeByUser(User user);

mapper(serviceImpl)调用:

/**
	 * 权限树byUser
	 */
	@Override
	public List<Permission> queryTreeByUser(User user) {
		List<Permission> list = userMapper.queryTreeByUser(user);
		return list;
	}

mapper.xml(sql层):

<!-- 查询树byUser -->
	<select id="queryTreeByUser" parameterType="user" resultType="permission">
	SELECT * FROM permission WHERE permission_id IN 
	(SELECT permission_id FROM role_permission WHERE role_id IN 
	(SELECT role_id FROM user_role WHERE user_id =#{user_id}))
	UNION
	SELECT * FROM permission WHERE permission_id IN 
	(SELECT permission_id FROM user_permission WHERE user_id =#{user_id})
	</select>

三、前端交互

用户主页加载时,执行js,加载权限树。 注意: 这里的treeNode转换函数,我的另一片文章有写:
使用EasyUI构建异步Tree

/**
 * tree转换函数
 * @param rows
 * @returns
 */

function convert(rows){
	function exists(rows, parentId){
		for(var i=0; i<rows.length; i++){
			if (rows[i].id == parentId) return true;
		}
		return false;
	}
	
	var nodes = [];
	// get the top level nodes
	for(var i=0; i<rows.length; i++){
		var row = rows[i];
		if (!exists(rows, row.parentId)){
			nodes.push({
				id:row.id,
				text:row.text,
			});
		}
	}
	
	var toDo = [];
	for(var i=0; i<nodes.length; i++){
		toDo.push(nodes[i]);
	}
	while(toDo.length){
		var node = toDo.shift();	// the parent node
		// get the children nodes
		for(var i=0; i<rows.length; i++){
			var row = rows[i];
			if (row.parentId == node.id){
				var child = {id:row.id,text:row.text,attributes:row.attributes};
				if (node.children){
					node.children.push(child);
				} else {
					node.children = [child];
				}
				toDo.push(child);
			}
		}
	}
	return nodes;
}
$(function(){
	   //定义动态菜单的动作
	   $("#aa").accordion({
			//height:400,
			weigth:"auto",
			border:false,
			align:'center'
		});
	  
	   //tree异步加载
	   /*debugger;*/
	   $("#tree").tree({
		    method:"post",
		    url:'queryTreeByUser', 
		    // The url will be mapped to NodeController class and getNodes method
		    lines:true,
			/*动画效果*/
			animate:true,
		    loadFilter : function(rows) {
				return convert(rows);
			},	
			onClick:function(rows){
				 var info = rows.id+rows.text+rows.state+
				 rows.checked+rows.attributes+rows.children;
				  var src=rows.attributes.url;
				   tname = rows.text;//标题
				   //3.判断选项卡是否已经创建
				   if($("#tt").tabs('exists',tname))
					 {
					   $("#tt").tabs('select',tname);
					   
					 }else{
						$("#tt").tabs('add',{
							title:tname,
							content:'<iframe frameborder=0 
							style=width:100%;height:100% src='+src+'></iframe>',
							closable:true,
						});
					 }
			}
		});
   });

完整项目地址(含文档及部署war包): https://github.com/leezhou999/bysj.git

后记:本文中的RBAC是个人项目中模块需要,因此暂未单独整理完整DEMO,需要的可自行研究完整项目。