前言:权限管理(控制)是OA,多角色,管理类系统必不可少的模块。shiro是spring的全家桶中一员,如果项目很小,如课程设计,毕业设计这类无需如此大的组件的话,可以考虑基于RBAC原理,手写一个
Q:什么是权限管理?
Q:什么是RBAC思想?(Role Based Acess Control)
Q:RBAC实现有几种常见方式?从简单到复杂(完整设计)分别需要多少张表?
以上问题自行探索后,阅读本文更佳。
效果图:
实现效果:
- 多角色即时授权管理等基本操作
- 复选框勾选,授权/取消授权
- 上一次授权结果显示
- 按角色批量授权
- 按具体用户定制(特殊、临时)授权
语言:java
IDE:Eclipse
JDK:1.7
Tomcat:7.0.81
MySQL:5.0
数据库图形化管理工具:SQLyog
前端依赖:js,jQuery,easyui
后端依赖:SSM(springMVC,spring,myBatis)
一、数据库表设计
至少需要5张表
第一种:基本5张表架构
第二种:扩展一张用户-权限表
1、user(用户表) :用户基本信息
2、role(角色表) :系统设计中包含的角色
3、permission(权限表) :系统功能(权限)
4、user_role(用户-角色关联表) :用户是什么角色
5、role_permission(角色-权限关联表) :某类角色有什么权限
6、user_permission(用户_权限关联表)(可选) : 某个用户有什么权限
二、后台设计
业务流程: 用户登录-》获取当前用户的角色-》查找该角色拥有的权限-》查找角色-权限表-》查找权限表-》获取对应权限
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,需要的可自行研究完整项目。