如果没有权限控制,系统的功能完全不设防,全部暴露在所有用户面前。用户登录以后可以使用系统中的所有功能。这是实际运行中不能接受的,所以权限控制系统的目标就是管理用户行为,保护系统功能。
资源就是系统中需要保护起来的功能。具体形式很多:URL 地址、controller方法、service 方法、页面元素等等都可以定义为资源使用权限控制系统保护起来。
一个功能复杂的项目会包含很多具体资源,成千上万都有可能。这么多资源逐个进行操作太麻烦了。为了简化操作,可以将相关的几个资源封装到一起,打包成一个“权限”同时分配给有需要的人。
对于一个庞大系统来说,一方面需要保护的资源非常多,另一方面操作系统的人也非常多。把资源打包为权限是对操作的简化,同样把用户划分为不同角色也是对操作的简化。否则直接针对一个个用户进行管理就会很繁琐。所以角色就是用户的分组、分类。先给角色分配权限,然后再把角色分配给用户,用户以这个角色的身份操作系统就享有角色对应的权限了。
系统中的用户其实是人操作系统时用来登录系统的账号、密码。
权限→资源:单向多对多
Java 类之间单向:从权限实体类可以获取到资源对象的集合,但是通过资源获取不到权限
数据库表之间多对多:
一个权限可以包含多个资源
一个资源可以被分配给多个不同权限
角色→权限:单向多对多
Java 类之间单向:从角色实体类可以获取到权限对象的集合,但是通过权限获取不到角色
数据库表之间多对多:
一个角色可以包含多个权限
一个权限可以被分配给多个不同角色
用户→角色:双向多对多
Java 类之间双向:可以通过用户获取它具备的角色,也可以看一个角色下包含哪些用户
数据库表之间:
一个角色可以包含多个用户
一个用户可以身兼数职
鉴于权限控制的核心是用户通过角色与权限进行关联,所以前面描述的权限控制系统可以提炼为一个模型:RBAC(Role-Based Access Control,基于角色的访问控制)。在 RBAC 模型中,一个用户可以对应多个角色,一个角色拥有多个权限,权限具体定义用户可以做哪些事情。
最基本的 RBAC 模型,RBAC 模型的核心部分,后面三种升级版 RBAC 模型也都是建立在 RBAC0 的基础上。
在 RBAC0 的基础上增加了角色之间的继承关系。角色 A 继承角色 B 之后将具备 B 的权限再增加自己独有的其他权限。比如:付费会员角色继承普通会员角色,那么付费会员除了普通会员的权限外还具备浏览付费内容的权限
在 RBAC0 的基础上进一步增加了角色责任分离关系。责任分离关系包含静态责任分离和动态责任分离两部分。
静态责任分离:给用户分配角色时生效
互斥角色:权限上相互制约的两个或多个角色就是互斥角色。用户只能被分配到一组互斥角色中的一个角色。例如:一个用户不能既有会计师角色又有审计师角色。
基数约束:
一个角色对应的访问权限数量应该是受限的
一个角色中用户的数量应该是受限的
一个用户拥有的角色数量应该是受限的
先决条件角色:用户想拥有 A 角色就必须先拥有 B 角色,从而保证用户拥有 X 权限的前提是拥有 Y 权限。
动态责任分离:用户登录系统时生效
一个用户身兼数职,在特定场景下激活特定角色
RBAC3 是在 RBAC0 的基础上同时添加 RBAC2 和 RBAC3 的约束,最全面、最复杂
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80034
Source Host : localhost:3306
Source Schema : project_crowd
Target Server Type : MySQL
Target Server Version : 80034
File Encoding : 65001
Date: 01/11/2023 11:36:13
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`id` int NOT NULL AUTO_INCREMENT,
`name` char(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
这个就不说啦!
展示
编号
名称
操作
js
// 声明专门的函数显示确认模态框
function showConfirmModal(roleArray) {
// 打开模态框
$("#confirmModal").modal("show");
// 清除旧的数据
$("#roleNameDiv").empty();
// 在全局变量范围创建数组用来存放角色id
window.roleIdArray = [];
// 遍历roleArray数组
for(var i = 0; i < roleArray.length; i++) {
var role = roleArray[i];
var roleName = role.roleName;
$("#roleNameDiv").append(roleName+"
");
var roleId = role.roleId;
// 调用数组对象的push()方法存入新元素
window.roleIdArray.push(roleId);
}
}
// 执行分页,生成页面效果,任何时候调用这个函数都会重新加载页面
function generatePage() {
// 1.获取分页数据
var pageInfo = getPageInfoRemote();
// 2.填充表格
fillTableBody(pageInfo);
}
// 远程访问服务器端程序获取pageInfo数据
function getPageInfoRemote() {
// 调用$.ajax()函数发送请求并接受$.ajax()函数的返回值
var ajaxResult = $.ajax({
"url": "role/get/page/info.json",
"type":"post",
"data": {
"pageNum": window.pageNum,
"pageSize": window.pageSize,
"keyword": window.keyword
},
"async":false,
"dataType":"json"
});
console.log(ajaxResult);
// 判断当前响应状态码是否为200
var statusCode = ajaxResult.status;
// 如果当前响应状态码不是200,说明发生了错误或其他意外情况,显示提示消息,让当前函数停止执行
if(statusCode != 200) {
layer.msg("失败!响应状态码="+statusCode+" 说明信息="+ajaxResult.statusText);
return null;
}
// 如果响应状态码是200,说明请求处理成功,获取pageInfo
var resultEntity = ajaxResult.responseJSON;
// 从resultEntity中获取result属性
var result = resultEntity.result;
// 判断result是否成功
if(result == "FAILED") {
layer.msg(resultEntity.message);
return null;
}
// 确认result为成功后获取pageInfo
var pageInfo = resultEntity.data;
// 返回pageInfo
return pageInfo;
}
// 填充表格
function fillTableBody(pageInfo) {
// 清除tbody中的旧的内容
$("#rolePageBody").empty();
// 这里清空是为了让没有搜索结果时不显示页码导航条
$("#Pagination").empty();
// 判断pageInfo对象是否有效
if(pageInfo == null || pageInfo == undefined || pageInfo.list == null || pageInfo.list.length == 0) {
$("#rolePageBody").append("抱歉!没有查询到您搜索的数据! ");
return ;
}
// 使用pageInfo的list属性填充tbody
for(var i = 0; i < pageInfo.list.length; i++) {
var role = pageInfo.list[i];
var roleId = role.id;
var roleName = role.name;
var checkboxTd = " ";
var numberTd = ""+(i+1)+" ";
var roleNameTd = ""+roleName+" ";
var checkBtn = "";
// 通过button标签的id属性(别的属性其实也可以)把roleId值传递到button按钮的单击响应函数中,在单击响应函数中使用this.id
var pencilBtn = "";
// 通过button标签的id属性(别的属性其实也可以)把roleId值传递到button按钮的单击响应函数中,在单击响应函数中使用this.id
var removeBtn = "";
var buttonTd = ""+checkBtn+" "+pencilBtn+" "+removeBtn+" ";
var tr = ""+numberTd+checkboxTd+roleNameTd+buttonTd+" ";
$("#rolePageBody").append(tr);
}
// 生成分页导航条
generateNavigator(pageInfo);
}
// 生成分页页码导航条
function generateNavigator(pageInfo) {
// 获取总记录数
var totalRecord = pageInfo.total;
// 声明相关属性
var properties = {
"num_edge_entries": 3,
"num_display_entries": 5,
"callback": paginationCallBack,
"items_per_page": pageInfo.pageSize,
"current_page": pageInfo.pageNum - 1,
"prev_text": "上一页",
"next_text": "下一页"
}
// 调用pagination()函数
$("#Pagination").pagination(totalRecord, properties);
}
// 翻页时的回调函数
function paginationCallBack(pageIndex, jQuery) {
// 修改window对象的pageNum属性
window.pageNum = pageIndex + 1;
// 调用分页函数
generatePage();
// 取消页码超链接的默认行为
return false;
}
展示调用的分页函数
$(function () {
//初始化数据
window.pageNum = 1;
window.pageSize = 5;
window.keyword = "";
//调用分页函数
generatePage();
$("#searchBtn").click(function () {
//获取关键字数据
window.keyword = $("#keywordInput").val();
//调用函数
generatePage();
});
})
创建模态框jsp:默认是放在最后的位置
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
点击添加展示模态框
//点击添加打开模态框
$("#showAddModalBtn").click(function () {
$("#addModal").modal("show");
});
前端操作:
//获取模态框中的数据,然后发送请求
$("#saveRoleBtn").click(function () {
let roleName = $.trim($("#addModal [name=roleName]").val());
// 发送异步请求
$.ajax({
"url": "role/save.json",
"type": "post",
"data": {
"name": roleName
},
"dataType":"json",
"success":function (response) {
let result = response.result;
if (result=="SUCCESS"){
layer.msg("操作成功!");
//重新加载分页
window.pageNum=999999;
generatePage();
}
if (result=="FAILED"){
layer.msg("操作失败!"+response.message)
}
},
"error":function (response) {
layer.msg(response.status+"" +response.statusText)
}
});
//关闭模态框
$("#addModal").modal("hide");
//清理模态框
$("#addModal [name=roleName]").val("")
});
controller
@ResponseBody
@RequestMapping(value = "/role/save.json")
public ResultEntity saveRole(Role role){
roleService.saveRole(role);
return ResultEntity.successWithoutData();
}
service
/**
* @description: 添加角色
* @author: 斗痘侠
* @date: 2023/11/1 19:21
* @param: role
**/
@Override
public void saveRole(Role role) {
roleMapper.insertSelective(role);
}