后台管理(二)
1、权限控制
如果没有权限控制,系统的功能完全不设防,全部暴露在所有用户面前。用户登录以后可以使用系统中的所有功能。这是实际运行中不能接受的,所以权限控制系统的目标就是管理用户行为,保护系统功能。
1.1、 定义资源
资源就是系统中需要保护起来的功能。具体形式很多:URL 地址、controller方法、service 方法、页面元素等等都可以定义为资源使用权限控制系统保护起来。
1.2、 创建权限
一个功能复杂的项目会包含很多具体资源,成千上万都有可能。这么多资源逐个进行操作太麻烦了。为了简化操作,可以将相关的几个资源封装到一起,打包成一个“权限”同时分配给有需要的人。
1.3、 创建角色
对于一个庞大系统来说,一方面需要保护的资源非常多,另一方面操作系统的人也非常多。把资源打包为权限是对操作的简化,同样把用户划分为不同角色也是对操作的简化。否则直接针对一个个用户进行管理就会很繁琐。所以角色就是用户的分组、分类。先给角色分配权限,然后再把角色分配给用户,用户以这个角色的身份操作系统就享有角色对应的权限了。
1.4 管理用户
系统中的用户其实是人操作系统时用来登录系统的账号、密码。
1.5、 建立关联关系
权限→资源:单向多对多
-
Java 类之间单向:从权限实体类可以获取到资源对象的集合,但是通过资源获取不到权限
-
数据库表之间多对多:
-
一个权限可以包含多个资源
-
一个资源可以被分配给多个不同权限
-
角色→权限:单向多对多
-
Java 类之间单向:从角色实体类可以获取到权限对象的集合,但是通过权限获取不到角色
-
数据库表之间多对多:
-
一个角色可以包含多个权限
-
一个权限可以被分配给多个不同角色
-
用户→角色:双向多对多
-
Java 类之间双向:可以通过用户获取它具备的角色,也可以看一个角色下包含哪些用户
-
数据库表之间:
-
一个角色可以包含多个用户
-
一个用户可以身兼数职
-
2、RBAC 权限模型
2.1 概念
鉴于权限控制的核心是用户通过角色与权限进行关联,所以前面描述的权限控制系统可以提炼为一个模型:RBAC(Role-Based Access Control,基于角色的访问控制)。在 RBAC 模型中,一个用户可以对应多个角色,一个角色拥有多个权限,权限具体定义用户可以做哪些事情。
2.2.1 RBAC0
最基本的 RBAC 模型,RBAC 模型的核心部分,后面三种升级版 RBAC 模型也都是建立在 RBAC0 的基础上。
2.2.2 RBAC1
在 RBAC0 的基础上增加了角色之间的继承关系。角色 A 继承角色 B 之后将具备 B 的权限再增加自己独有的其他权限。比如:付费会员角色继承普通会员角色,那么付费会员除了普通会员的权限外还具备浏览付费内容的权限
2.2.3 RBAC2
在 RBAC0 的基础上进一步增加了角色责任分离关系。责任分离关系包含静态责任分离和动态责任分离两部分。
-
静态责任分离:给用户分配角色时生效
-
互斥角色:权限上相互制约的两个或多个角色就是互斥角色。用户只能被分配到一组互斥角色中的一个角色。例如:一个用户不能既有会计师角色又有审计师角色。
-
基数约束:
-
一个角色对应的访问权限数量应该是受限的
-
一个角色中用户的数量应该是受限的
-
一个用户拥有的角色数量应该是受限的
-
-
先决条件角色:用户想拥有 A 角色就必须先拥有 B 角色,从而保证用户拥有 X 权限的前提是拥有 Y 权限。
-
-
动态责任分离:用户登录系统时生效
-
一个用户身兼数职,在特定场景下激活特定角色
-
2.2.4 RBAC3
RBAC3 是在 RBAC0 的基础上同时添加 RBAC2 和 RBAC3 的约束,最全面、最复杂
3、角色维护:
3.1、创建角色表
/*Navicat Premium Data TransferSource Server : localhostSource Server Type : MySQLSource Server Version : 80034Source Host : localhost:3306Source Schema : project_crowdTarget Server Type : MySQLTarget Server Version : 80034File Encoding : 65001Date: 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;
3.2、使用插件逆向工程
这个就不说啦!
3.3、分页查询:
3.3.1、关键字查询
展示
<div class="table-responsive"><table class="table table-bordered"><thead><tr><th width="30"><input type="checkbox"></th><th width="50">编号</th><th>名称</th><th width="100">操作</th></tr></thead><tbody id="rolePageBody"></tbody><tfoot><tr><td colspan="6" align="center"><div id="Pagination" class="pagination"><!-- 这里显示分页 --></div></td></tr></tfoot></table>
</div>
js
// 声明专门的函数显示确认模态框
function showConfirmModal(roleArray) {// 打开模态框$("#confirmModal").modal("show");// 清除旧的数据$("#roleNameDiv").empty();// 在全局变量范围创建数组用来存放角色idwindow.roleIdArray = [];// 遍历roleArray数组for(var i = 0; i < roleArray.length; i++) {var role = roleArray[i];var roleName = role.roleName;$("#roleNameDiv").append(roleName+"<br/>");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);// 判断当前响应状态码是否为200var statusCode = ajaxResult.status;// 如果当前响应状态码不是200,说明发生了错误或其他意外情况,显示提示消息,让当前函数停止执行if(statusCode != 200) {layer.msg("失败!响应状态码="+statusCode+" 说明信息="+ajaxResult.statusText);return null;}// 如果响应状态码是200,说明请求处理成功,获取pageInfovar resultEntity = ajaxResult.responseJSON;// 从resultEntity中获取result属性var result = resultEntity.result;// 判断result是否成功if(result == "FAILED") {layer.msg(resultEntity.message);return null;}// 确认result为成功后获取pageInfovar pageInfo = resultEntity.data;// 返回pageInforeturn pageInfo;
}// 填充表格
function fillTableBody(pageInfo) {// 清除tbody中的旧的内容$("#rolePageBody").empty();// 这里清空是为了让没有搜索结果时不显示页码导航条$("#Pagination").empty();// 判断pageInfo对象是否有效if(pageInfo == null || pageInfo == undefined || pageInfo.list == null || pageInfo.list.length == 0) {$("#rolePageBody").append("<tr><td colspan='4' align='center'>抱歉!没有查询到您搜索的数据!</td></tr>");return ;}// 使用pageInfo的list属性填充tbodyfor(var i = 0; i < pageInfo.list.length; i++) {var role = pageInfo.list[i];var roleId = role.id;var roleName = role.name;var checkboxTd = "<td><input id='"+roleId+"' class='itemBox' type='checkbox'></td>";var numberTd = "<td>"+(i+1)+"</td>";var roleNameTd = "<td>"+roleName+"</td>";var checkBtn = "<button type='button' class='btn btn-success btn-xs'><i class=' glyphicon glyphicon-check'></i></button>";// 通过button标签的id属性(别的属性其实也可以)把roleId值传递到button按钮的单击响应函数中,在单击响应函数中使用this.idvar pencilBtn = "<button id='"+roleId+"' type='button' class='btn btn-primary btn-xs pencilBtn'><i class=' glyphicon glyphicon-pencil'></i></button>";// 通过button标签的id属性(别的属性其实也可以)把roleId值传递到button按钮的单击响应函数中,在单击响应函数中使用this.idvar removeBtn = "<button id='"+roleId+"' type='button' class='btn btn-danger btn-xs removeBtn'><i class=' glyphicon glyphicon-remove'></i></button>";var buttonTd = "<td>"+checkBtn+" "+pencilBtn+" "+removeBtn+"</td>";var tr = "<tr>"+numberTd+checkboxTd+roleNameTd+buttonTd+"</tr>";$("#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();});})
3.3.2、添加角色
创建模态框jsp:默认是放在最后的位置
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<div id="addModal" class="modal fade" 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" aria-label="Close"><span aria-hidden="true">×</span></button><h4 class="modal-title">创筹网系统弹窗</h4></div><div class="modal-body"><form class="form-signin" role="form"><div class="form-group has-success has-feedback"><inputtype="text" name="roleName"class="form-control" id="inputSuccess4" placeholder="请输入角色名称" autofocus></div></form></div><div class="modal-footer"><button id="saveRoleBtn" type="button" class="btn btn-primary"> 保存</button></div></div></div>
</div>
点击添加展示模态框
//点击添加打开模态框$("#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<String> saveRole(Role role){roleService.saveRole(role);return ResultEntity.successWithoutData();}
service
/*** @description: 添加角色* @author: 斗痘侠* @date: 2023/11/1 19:21* @param: role**/@Overridepublic void saveRole(Role role) {roleMapper.insertSelective(role);}
相关文章:
后台管理(二)
1、权限控制 如果没有权限控制,系统的功能完全不设防,全部暴露在所有用户面前。用户登录以后可以使用系统中的所有功能。这是实际运行中不能接受的,所以权限控制系统的目标就是管理用户行为,保护系统功能。 1.1、 定义资源 资源就…...

反转链表II(C++解法)
题目 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left < right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。 示例 1: 输入:head [1,2,3,4,5], left 2, right 4 输出:[1…...

记一次 logback 没有生成独立日志文件问题
背景 在新项目发布后发现日志文件并没有按照期望的方式独立开来,而是都写在了 application.log 文件中。 问题展示 日志文件: 项目引入展示: <include resource"paas/sendinfo/switch/client/sendinfo-paas-switch-client-log.…...
数据库强化(1.视图)
1.什么是视图 视图是指计算机数据库中的视图,是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的…...
Mysql与SeaweedFS数据不同步问题产生原因及解决办法
文章目录 Mysql与SeaweedFS数据不同步问题的探究与解决问题背景原因探究不一致的写操作缺乏事务管理 解决方案引入分布式事务处理使用消息队列 实践演示(python代码)结论 Mysql与SeaweedFS数据不同步问题的探究与解决 问题背景 在数据库和文件存储系统…...
Kotlin apply和with用法和区别
apply apply 是 Kotlin 标准库中的一个函数,它允许你在对象上执行一系列操作,然后返回该对象自身。它的语法结构如下: fun <T> T.apply(block: T.() -> Unit): T这个函数接受一个 lambda 表达式作为参数,该 lambda 表达…...

springboot通过aop自定义注解@Log实现日志打印
springboot通过aop自定义注解Log实现日志打印 文章目录 效果图实操步骤1.引入依赖2.自定义日志注解3.编写日志切面类4.UserController5.运行 效果图 实操步骤 注意,本代码在springboot环境下运行,jdk1.8 1.引入依赖 <dependency><groupId>…...
k8spod详解其二
一,资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小,以及其他类型的资源。 当为 Pod 中的容器指定了 request 资源时,调度器就使用该信息来决定将 Pod 调度到哪个节点上。当还为…...
golang包的管理
Go语言中包的使用 Go语言使用包(package)这种语法元素来组织源码,所有语法可见性均定义在package这个级别,与Java 、python等语言相比,这算不上什么创新,但与C传统的include相比,则是显得“先进…...

Windows10安装Anaconda与Pytorch的记录
这是一篇关于安装Anaconda和Pytorch的记录与复盘,写的原因是我电脑恢复系统之后东西全没了,再装Pytorch的时候一脸懵逼忘了怎么弄了,写篇记录以备我下一次安装。 1、Anaconda的安装 1.1、Anaconda安装包下载 下载链接: Free Download | An…...

图解Kafka高性能之谜(五)
高性能的多分区、冗余副本集群架构 高性能网络模型NIO 简单架构设计: 详细架构设计: 高性能的磁盘写技术 高性能的消息查找设计 索引文件定位使用跳表的设计 偏移量定位消息时使用稀疏索引: 高响应的磁盘拷贝技术 kafka采用sendFile()的…...

opencv在linux上调用usb摄像头进行拍照
功能 1.按照指定的文件名创建文件夹,创建之前判断该文件夹是否存在 2.调用摄像头按可调整窗口大小的方式显示 3.按esc退出摄像头画面 4.按p保存当前摄像头的画面,并按当前时间为照片的名字进行保存打开终端查看是否有摄像头 ls /dev/video*一般video1就…...
软考之知识产品+例题
知识产权 保护期限 公民作品 没有限制 署名权、修改权、保护作品完整权 作者终生及其死亡后的第 50 年的 12月31日 发表权、使用权、获得报酬权 单位作品 首次发表后的第 50 年的 12月31 日,若未发表则不受保护 发表权、使用权、获得报酬权 公民软件作品 没…...

玩了一下 Jenkins,最新版本 + JDK11
背景 今年五月的时候玩了一下 Jenkins,最新版本 2.414.3 ,JDK 11 。本机有两个 JDK,只放到 Tomcat 里面了,看到了一个启动页面,后面有其他事情就忘记了。最近又想起来,觉得还是应该玩一下这么有技术含量的…...

自定义的卷积神经网络模型CNN,对图片进行分类并使用图片进行测试模型-适合入门,从模型到训练再到测试,开源项目
自定义的卷积神经网络模型CNN,对图片进行分类并使用图片进行测试模型-适合入门,从模型到训练再到测试:开源项目 开源项目完整代码及基础教程: https://mbd.pub/o/bread/ZZWclp5x CNN模型: 1.导入必要的库和模块&…...
C# 使用.NET的SocketAsyncEventArgs实现高效能多并发TCPSocket通信
简介: SocketAsyncEventArgs是一个套接字操作得类,主要作用是实现socket消息的异步接收和发送,跟Socket的BeginSend和BeginReceive方法异步处理没有多大区别,它的优势在于完成端口的实现来处理大数据的并发情况。 BufferManager类…...
设计模式——观察者模式(Observer Pattern)+ Spring相关源码
文章目录 一、观察者模式定义二、例子2.1 菜鸟教程例子2.1.1 定义观察者2.1.2 定义被观察对象2.1.3 使用 2.2 JDK源码 —— Observable2.2.1 观察者接口Observer2.2.1 被观察者对象Observable 2.3 Spring源码 —— AbstractApplicationContext2.3.1 观察者2.3.2 被观察者 2.3 G…...

openpnp - code review - 开机对话框历史记录和贡献者名单
文章目录 openpnp - code review - 开机对话框历史记录和贡献者名单概述笔记D:\my_openpnp\openpnp_dev_2022_0801\src\main\java\org\openpnp\gui\AboutDialog.javaEND openpnp - code review - 开机对话框历史记录和贡献者名单 概述 偶然发现, 自己打包后的openpnp, 开机后…...
JavaSE22——HashMap
集合框架_HashMap 一、概述 HashMap 是用于存储 Key-Value 键值对的集合。 (1)HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,所以具有很快的访问速度,但遍历顺序不确定。 (2&…...

「图像 merge」无中生有制造数据
在进行一个新项目的时候,往往缺少一些真实数据,导致没办法进行模型训练,这时候就需要算法工程师自行制作一些数据了,比如这篇文章分享的 bag 目标检测,在检测区域没有真实的 bag数据 此时,就可以采用图像拼…...

“一代更比一代强”:现代 RAG 架构的演进之路
编者按: 我们今天为大家带来的文章,作者的观点是:RAG 技术的演进是一个从简单到复杂、从 Naive 到 Agentic 的系统性优化过程,每一次优化都是在试图解决无数企业落地大语言模型应用时出现的痛点问题。 文章首先剖析 Naive RAG 的基…...

基于Django开发的运动商城系统项目
运动商城系统项目描述 运动商城系统是一个基于现代Web技术构建的电子商务平台,专注于运动类商品的在线销售与管理。该系统采用前后端分离架构,前端使用Vue.js实现动态交互界面,后端基于Django框架提供RESTful API支持,数据库采用…...
数论——同余问题全家桶3 __int128和同余方程组
数论——同余问题全家桶3 __int128和同余方程组 快速读写和__int128快速读写__int128 中国剩余定理和线性同余方程组中国剩余定理(CRT)中国剩余定理OJ示例模板题曹冲养猪 - 洛谷模板题猜数字 - 洛谷 扩展中国剩余定理扩展中国剩余定理OJ示例模板题扩展中国剩余定理(…...
JavaSec-SPEL - 表达式注入
简介 SPEL(Spring Expression Language):SPEL是Spring表达式语言,允许在运行时动态查询和操作对象属性、调用方法等,类似于Struts2中的OGNL表达式。当参数未经过滤时,攻击者可以注入恶意的SPEL表达式,从而执行任意代码…...

摆脱硬件依赖:SkyEye在轨道交通中的仿真应用
在城市轨道交通系统中,信号系统承担着确保列车安全、高效运行的关键任务。从排列进路、信号开放,到终点折返与接发车,几乎每一个调度动作背后都依赖于信号系统的精密控制与实时响应。作为信号系统的重要组成部分,目标控制器&#…...

阿里云事件总线 EventBridge 正式商业化,构建智能化时代的企业级云上事件枢纽
作者:肯梦、稚柳 产品演进历程:在技术浪潮中的成长之路 早在 2018 年,Gartner 评估报告便将事件驱动模型(Event-Driven Model)列为十大战略技术趋势之一,指出事件驱动架构(EDA,Eve…...

兰亭妙微 | 医疗软件的界面设计能有多专业?
从医疗影像系统到手术机器人控制界面,从便携式病原体检测设备到多平台协同操作系统,兰亭妙微为众多医疗设备研发企业,打造了兼具专业性与可用性的交互界面方案。 我们不仅做设计,更深入理解医疗场景的实际需求: 对精…...

云原生 DevOps 实践路线:构建敏捷、高效、可观测的交付体系
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:DevOps 与云原生的深度融合 在传统软件工程范式下,开发与运维之间存在天然的壁垒。开发希望尽快…...

gateway 网关 路由新增 (已亲测)
问题: 前端通过gateway调用后端接口,路由转发失败,提示404 not found 排查: 使用 { "href":"/actuator/gateway/routes", "methods":[ "POST", "GET" ] } 命令查看路由列表&a…...
impala中更改公网ip为内网ip
实际有时候需求中需要将公网的impala监听ip改为内网的ip 步骤 1,更改配置文件中的ip 1,更改/etc/default/impala中的ip配置重启服务即可在hive元数据同一个节点上要启动sudo service impala-state-store restartsudo service impala-catalog restart所有…...