项目计划
- RBAC 角色模块
第一章 RBAC权限模型
1.1 基础表模型
1.概念
权限管理,这是每个软件系统都会涉及到的,而且权限管理的需求本质往往都是一样,不同的角色拥有不同的权限,只要你充当了某个角色,你就拥有了相对应的功能。
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。
简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。
这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般都是多对多的关系
1.2 扩展表模型
1.2.1 扩展用户组
1.2.2 扩展更多权限控制元素
1.2.3 完整扩展模型
1.3 级别0
RBAC级别 - RABC0
RBAC0的模型中包括用户(U)、角色(R)和许可权(P)等3类实体集合。
RABC0权限管理的核心部分,其他的版本都是建立在0的基础上的,看一下类图:
1.4 级别1
RBAC1,基于RBAC0模型,引入角色间的继承关系,即角色上有了上下级的区别,角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承。
这种模型合适于角色之间的层次明确,包含明确。
1.5 级别2
RBAC级别 - RABC2
RBAC2,基于RBAC0模型的基础上,进行了角色的访问控制。
RBAC2模型中添加了责任分离关系。RBAC2的约束规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束与用户-角色-权限关系一起决定了RBAC2模型中用户的访问许可,此约束有多种。
互斥角色 :同一用户只能分配到一组互斥角色集合中至多一个角色,支持责任分离的原则。互斥角色是指各自权限互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。常举的例子:在审计活动中,一个角色不能同时被指派给会计角色和审计员角色。
基数约束 :一个角色被分配的用户数量受限;一个用户可拥有的角色数目受限;同样一个角色对应的访问权限数目也应受限,以控制高级权限在系统中的分配。例如公司的领导人有限的;
先决条件角色 :可以分配角色给用户仅当该用户已经是另一角色的成员;对应的可以分配访问权限给角色,仅当该角色已经拥有另一种访问权限。指要想获得较高的权限,要首先拥有低一级的权限。
运行时互斥 :例如,允许一个用户具有两个角色的成员资格,但在运行中不可同时激活这两个角色。
1.6 级别3
RBAC级别 - RABC3
RBAC3,也就是最全面级的权限管理,它是基于RBAC2的基础上,将RBAC1和RBAC2进行整合了,最全面,也最复杂的
总结
任何系统中都会涉及到权限管理的模块,无论复杂简单,我们都可以通过以RBAC模型为基础,进行相关灵活运用来解决我们的问题
基本模型RBAC0定义了完全支持RBAC概念的任何系统的最低需求。
RBAC1和RBAC2两者都包含RBAC0,但各自都增加了独立的特点,它们被称为高级模型。
RBAC1中增加了角色分级的概念,一个角色可以从另一个角色继承许可权。
RBAC2中增加了一些限制,强调在RBAC的不同组件中在配置方面的一些限制。
RBAC3称为统一模型,它包含了RBAC1和RBAC2,利用传递性,也把RBAC0包括在内。这些模型构成了RBAC96模型族。
1.7 ACL模型
ACL(Access Controller List)模型
访问控制列表,是前几年盛行的一种权限设计,它的核心在于用户直接和权限挂钩。
RBAC的核心是用户和角色关联,而角色代表了权限,这样设计的优势在于使得对用户而言,只需角色即可以,而某角色可以拥有各种各样的权限并可继承。
ACL和RBAC相比缺点在于由于用户和权限直接挂钩,导致在授予时的复杂性,虽然可以利用组来简化这个复杂性,但仍然会导致系统不好理解,而且在取出判断用户是否有该权限时比较的困难,一定程度上影响了效率。
1.8 基于RBAC模型的权限框架
Apache Shiro
参考官网视频: http://www.gulixueyuan.com/course/45
Spring Security
可以与Spring框架无缝集成
SELinux
第二章 查询角色
2.1 控制器
/**
* 要什么数据发请求获取即可
* @return
*/
@GetMapping("/index.html")
public String index(){
return "admin/role/list";
}
/**
* 获取所有角色列表。我们只返回有用的数据
*/
@ResponseBody
@GetMapping("/list")
public PageInfo<TRole> roleList(@RequestParam(value = "pn", defaultValue = "1") Integer pn,
@RequestParam(value = "ps", defaultValue = "" + AppConst.PAGE_SIZE) Integer ps,
@RequestParam(value = "condition", defaultValue = "") String condition){
PageHelper.startPage(pn, ps);
List<TRole> roles = adminRoleService.selectAll(condition);
PageInfo<TRole> pageInfo = new PageInfo<>(roles,AppConst.NAV_PAGE_NUM);
return pageInfo;
}
2.2 业务层
@Override
public List<TRole> listByCondition(String condition) {
TRoleExample example = new TRoleExample();
if(!StringUtils.isEmpty(condition)) {
example.createCriteria().andNameLike("%"+condition+"%");
try {
int id = Integer.parseInt(condition);
Criteria criteria = example.createCriteria();
criteria.andIdEqualTo(id);
example.or(criteria);
} catch (NumberFormatException e) {
}
}
return tRoleMapper.selectByExample(example);
}
2.3 测试
2.3.1 安装浏览器web端开发助手,可以美化json数据。
WEB前端助手(FeHelper)_v2019.01.0714.crx
2.4 异步请求代码
<script type="text/javascript">
var pageN = {pn:1,condition:"",pages:""};
$(function(){
//1、发请求,获取所有的角色数据
initRoleData();
//2、将这些数据解析显示在页面的指定位置
})
function initRoleData(){
$.get("${appPath}/role/list",{pn:pageN.pn,condition:pageN.condition},function(data){
//1、拿到数据以后,解析数据,放在页面指定位置(js对DOM的CRUD)
//alert(typeof content); // Object
//var str = JSON.stringify(content); //将js对象转为字符串
//alert(typeof content); // String
console.log(data);
pageN.pages = data.pages;
//2、展示表格数据
buildTableContent(data.list);
//3、展示分页条数据
buildPageNav(data);
});
}
//2、构建分页条
function buildPageNav(nav){
$("ul.pagination").empty();
var firstPage = "<li pn='1'><a href='#'>首页</a></li>";
var prePage = "<li pn='"+nav.prePage+"'><a href='#'>上一页</a></li>";
var nextPage = "<li pn='"+nav.nextPage+"'><a href='#'>下一页</a></li>";
var lastPage = "<li pn='"+nav.pages+"'><a href='#'>末页</a></li>";
$("ul.pagination").append(firstPage);
if(nav.hasPreviousPage){
$("ul.pagination").append(prePage);
}
//中间的连续分页
$.each(nav.navigatepageNums,function(){
//判断正在遍历的页码,是否当前页,如果是高亮显示
var cur = "";
if(this == nav.pageNum){ //是当前页
cur = "<li pn='"+this+"' class='active'><a href='#'>"+this+"</a></li>";
}else{
cur = "<li pn='"+this+"'><a href='#'>"+this+"</a></li>";
}
$("ul.pagination").append(cur);
});
if(nav.hasNextPage){
$("ul.pagination").append(nextPage);
}
$("ul.pagination").append(lastPage);
}
//1、构建表格数据
function buildTableContent(content){
//1、清除掉之前数据
$("#contentTable tbody").empty();
//console.log(content);
//var str = JSON.stringify(content);//将js对象转为字符串
//$("#contentTable tbody").append(str);
$.each(content,function(){
//每一个数据都要成为一个tr放在表格中
var btnGroup = $("<td></td>");
btnGroup.append("<button rid='"+this.id+"' class='btn btn-success btn-xs glyphicon glyphicon-check'></button> ")
btnGroup.append("<button rid='"+this.id+"' class='itemEditBtn btn btn-primary btn-xs glyphicon glyphicon-pencil'></button> ")
btnGroup.append("<button rid='"+this.id+"' class='itemDeleteBtn btn btn-danger btn-xs glyphicon glyphicon-remove'></button> ");
console.log(this);
var tr = $("<tr></tr>");//创建这个元素
tr.append("<td>"+this.id+"</td>");
tr.append("<td><input type='checkbox'></td>");
tr.append("<td>"+this.name+"</td>");
tr.append(btnGroup);
tr.appendTo("#contentTable tbody");
})
}
// $(找到元素).click():只能给页面已经存在的元素绑事件,未来动态添加到页面的绑不上(后来通过dom的CRUD给页面放的元素都不能绑上事件)
/* $("ul.pagination li").click(function(){
alert("111");
}); */
//$(父/祖先元素选择器).on(事件名,"要绑定事件的元素选择器",回调函数)
$("ul.pagination").on("click","li",function(){
var pn = $(this).attr("pn");
pageN.pn = pn;
initRoleData();//初始化数据
});
//点击查询按钮
$("#searchBtn").click(function(){
//1、获取条件值
var condition = $("#searchParam").val();
pageN.condition = condition;
initRoleData();
});
</script>
第三章 模态框
3.1 查看文档
3.4 基本使用
//点击打开角色添加模态框
$("#popAddModalBtn").click(function(){
$('#roleAddModal').modal("show");
});
//点击打开角色添加模态框
$("#popAddModalBtn").click(function(){
$('#roleAddModal').modal({
show:true, //打开
backdrop:'static'
});
});
<div class="modal fade" id="roleAddModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
<h4 class="modal-title">角色新增</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label>角色名</label>
<input id="roleName" class="form-control" placeholder="输入角色名">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" οnclick="saveRole()">保存</button>
</div>
</div>
</div>
</div>
第四章 角色添加
4.1 控制器
/**
* 角色添加
* @param role
* @return
*/
@ResponseBody
@PostMapping("/add")
public String roleSave(TRole role){
adminRoleService.addRole(role);
return "ok";
}
4.2 业务层
@Override
public void addRole(TRole role) {
roleMapper.insertSelective(role);
}
4.3 Postman测试
https://www.getpostman.com/ 下载安装(另外一个Restlet Client)
Postman-win64-6.6.1-Setup.exe
4.4 ajax代码
//保存角色
function saveRole(){
//1、拿到数据
var roleName = $("#roleName").val();
//2、发送请求
$.post("${appPath}/role/add",{name:roleName},function(data){
if(data == "ok"){
//保存成功
//1、关闭模态框
$('#roleAddModal').modal('hide');
//2、去最后一页
pageN.pn = pageN.pages+1;
initRoleData();
}
})
}
第五章 角色修改
5.1 点击按钮
<button rid='"+this.id+"' class='itemEditBtn btn btn-primary btn-xs glyphicon glyphicon-pencil'></button>
//点击修改按钮弹出模态框
$("tbody").on("click",".itemEditBtn",function(){
//1、打开模态框
$("#roleUpdateModal").modal({
show:true,
backdrop:'static'
});
//2、查询当前要修改用户的详细信息并回显
$.get("${appPath}/role/get",{id:$(this).attr("rid")},function(data){
//console.log(data);
$("#roleUpdateForm input[name='name']").val(data.name);
$("#roleUpdateForm input[name='id']").val(data.id);
});
});
5.2 显示模态框,并回显数据
<!-- 修改模态框 -->
<div class="modal fade" id="roleUpdateModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
<h4 class="modal-title">角色修改</h4>
</div>
<div class="modal-body">
<form id="roleUpdateForm">
<div class="form-group">
<label>角色名</label>
<input type="hidden" name="id"/>
<input class="form-control" name="name" placeholder="输入角色名">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" οnclick="updateRole()">修改</button>
</div>
</div>
</div>
</div>
5.3 提交修改
function updateRole(){
//1、修改角色
//1)、收集页面需要发送给服务器的数据
var rname = $("#roleUpdateForm input[name='name']").val();
var rid = $("#roleUpdateForm input[name='id']").val();
//2)、组装好数据给服务器发请求
$.post("${appPath}/role/update",{id:rid,name:rname},function(data){
if(data == "ok"){
//1、成功
//关闭模态框
$("#roleUpdateModal").modal('hide');
initRoleData();
}
})
//3)、处理服务器响应回来的数据
}
5.4 修改处理
5.4.1 控制器
//查询单个角色
@ResponseBody
@GetMapping("/get")
public TRole getRole(@RequestParam("id")Integer id){
return adminRoleService.getRoleById(id);
}
//接口
@ResponseBody
@PostMapping("/update")
public String roleUpdate(TRole role){
adminRoleService.updateRole(role);
return "ok";
}
5.4.2 服务层
@Override
public TRole getRoleById(Integer id) {
return roleMapper.selectByPrimaryKey(id);
}
@Override
public void updateRole(TRole role) {
roleMapper.updateByPrimaryKeySelective(role);
}
第六章 角色删除
6.1 控制器
@ResponseBody
@GetMapping("/delete")
public String delete (@RequestParam("ids")String ids){
//1、处理ids为要删除的用户的集合
List<Integer> userIds = new ArrayList<>();
String[] split = ids.split(",");
for (String str : split) {
int id;
try {
id = Integer.parseInt(str);
userIds.add(id);
} catch (NumberFormatException e) {
}
}
adminRoleService.deleteRoles(userIds);
return "ok";
}
6.2 服务层
@Override
public void deleteRoles(List<Integer> userIds) {
TRoleExample example = new TRoleExample();
example.createCriteria().andIdIn(userIds);
roleMapper.deleteByExample(example);
}
6.3 页面ajax代码
//单个删除
$("tbody").on("click",".itemDeleteBtn",function(){
var id = $(this).attr("rid");
var index = layer.confirm("确认删除吗?",{btn:["是的","NO"]},function(){
$.get("${appPath}/role/delete?ids="+id,function(data){
//删除成功,刷新数据
initRoleData();
layer.close(index);
});
},function(){});
})