【REST2SQL】14 基于角色的数据权限设计与实现

【REST2SQL】01RDB关系型数据库REST初设计
【REST2SQL】02 GO连接Oracle数据库
【REST2SQL】03 GO读取JSON文件
【REST2SQL】04 REST2SQL第一版Oracle版实现
【REST2SQL】05 GO 操作 达梦 数据库
【REST2SQL】06 GO 跨包接口重构代码
【REST2SQL】07 GO 操作 Mysql 数据库
【REST2SQL】08 日志重构增加输出到文件log.txt
【REST2SQL】09 给Go的可执行文件exe加图标和版本信息等
【REST2SQL】10 REST2SQL操作指南
【REST2SQL】11 基于jwt-go生成token与验证
【REST2SQL】12 REST2SQL增加Token生成和验证
【REST2SQL】13 用户角色功能权限设计


0 前言废话

0.1 常见的权限管理模型

  • ACL ACL的全称是 Access Control List (访问控制列表)
    ACL 是最简单的权限管理模型之一。它基于对象与主体之间的关系来控制访问权限。ACL 将权限直接与用户或用户组相关联,管理员直接给用户授予某些权限即可。
    这种模型适用于小型和简单系统,其中权限控制较为简单,并且角色和权限的变化较少。

  • RBAC(Role-Based Access Control,基于角色的访问控制)
    RBAC 是应用最广泛的权限管理模型之一。它通过定义角色和角色的权限集合来管理访问控制。用户被分配到角色,角色与权限相关联,从而精确地控制用户对系统资源的访问。
    RBAC 适用于大型系统,特别是那些需要灵活、可扩展的权限管理的场景。使用 RBAC 可以简化权限管理的复杂性并提高系统的安全性。
    RBAC权限管理的模式,最适合公司内部的管理系统,不适合对外互联网用户的系统。

  • ABAC(Attribute-Based Access Control,基于属性的访问控制)
    ABAC(Attribute-Based Access Control,基于属性的访问控制),又称为PBAC(Policy-Based Access Control,基于策略的访问控制),或CBAC(Claims-Based Access Control,基于声明的访问控制)。ABAC 是一种基于属性的权限管理模型,它根据多个属性(如用户属性、环境条件、时间等)来进行访问控制决策。ABAC 通过定义策略来决定用户是否有权访问特定的资源。
    这种模型适合于需要更细粒度、动态和灵活的访问控制的场景。ABAC 在复杂的环境中可以提供高度的可配置性和可扩展性。

0.2 数据权限分级

  • 系统级
  • 对象级,表或视图
  • 行级,表或视图的行
  • -列级,表或视图的具体字段

1 数据权限设计

权限管理的核心是角色,角色上接用户,下接功能,功能关联数据。
权限管理的5大实体 : 用户-角色=页面#数据,角色-数据对象关系
实体之间的关系:用户1对多角色,角色1对多页面,页面1对多数据,角色1对多数据。
实体及关系图如下:
在这里插入图片描述
此数据权限设计兼顾了灵活与通用。

  • 支持一个用户多角色,多角色都关联同一数据对象时,用户的数据权限是多个角色数据权限的并集。比如一个用户 user1 分配了2个角色 role1 和 role2,则user1 可以查阅 role1 和 role2 所有数据。
  • 根据角色的功能权限自动获取相关数据对象(视图或表),并在角色上定义数据权限约束 Where条件。

1.1 权限实体(表)设计

  • 用户表 s_user (p_id, s_name, s_passw…)
  • 角色表 s_role (p_id, s_name…)
  • 页面表 s_menu(p_id, s_name,s_page…)
  • 数据(对象)视图 user_tabs_views
  • 角色-数据权限表s_role_data(pf_role,pf_data,s_where)
    数据对象包括表和视图。
-- Oracle 数据库的表和视图
CREATE OR REPLACE VIEW USER_TABS_VIEWS AS
SELECT TABLE_NAME,TABLE_TYPE,COMMENTS,tab_cnt(table_name) as rowcount
FROM user_tab_comments
order by table_type,table_name;
comment on table USER_TABS_VIEWS is '用户表及视图';

1.2 权限关系(表)设计

  • 用户角色关系表 s_user_role (pf_user,pf_role…)
  • 角色功能权限关系表 s_role_menu (pf_role,pf_menu…)
  • 页面数据对象关系表 s_menu_data (pf_menu,pf_data…)
  • 角色数据对象权限关系表 s_role_data (PF_ROLE,PF_DATA,s_where,s_note…)
    角色项下所有功能页面关联的数据对象的集合(页面和数据是多对多的关系,此集合不去除了重复的数据对象)

1.3 角色数据权限视图设计

其它视图参阅 【REST2SQL】13 用户角色功能权限设计

  • 角色关联的数据对象视图,就是角色功能权限用到的数据对象。
	create or replace view role_data_v as
	select distinct m.pf_role, d.pf_data
	from S_MENU_DATA d
	left join s_role_menu m on m.pf_menu = d.pf_menu
	where m.pf_role is not null;
	comment on table ROLE_DATA_V is '角色关联的数据对象视图';
  • 角色数据权限重置连接表,就是角色功能权限或功能数据对象发生变化时,角色当前数据权限和应该拥有的数据权限对照视图
create or replace view role_data_join_v as
select "CROLE","CDATA","HROLE","HDATA"
from ( select pf_role as crole, pf_data as cdata from S_ROLE_DATA  ) c
full outer join (select pf_role as hrole, pf_data as hdata from role_data_v  ) h
 on h.hdata = c.cdata  and h.hrole = c.crole;
comment on table ROLE_DATA_JOIN_V is '角色数据权限重置连接表';

1.4 角色数据权限更新的存储过程

角色-功能权限或功能-数据对象发生变化时,执行此存储过程变更角色的数据权限,增加或删除角色的数据对象权限。

	CREATE OR REPLACE PROCEDURE reset_role_data
	IS
	--PRAGMA AUTONOMOUS_TRANSACTION;
	BEGIN
	  
	    -- 这里放置处理数据的代码
	    NULL; -- 示例中,我们不执行任何操作
	
	    for rec in ( select crole,cdata,hrole,hdata from role_data_join_v ) loop
	      if rec.crole is null then
	        --当前角色插入数据对象
	        dbms_output.put_line('A 当前角色增加数据对象...');
	        insert into s_role_data (pf_role,pf_data) values (rec.hrole,rec.hdata);
	      end if;
	
	       if rec.hrole is null then
	         --当前角色删除数据对象
	         dbms_output.put_line('B 当前角色删除数据对象...');
	         delete from s_role_data where pf_role = rec.crole and pf_data = rec.cdata;
	      end if;
	    end loop;
	
	    --提交数据操作数据
	    --commit;
	
	END;

1.5 触发器设计

  • 功能s_menu_data数据对象变化时触发器,功能页面数据对象变化时触发
	create or replace trigger s_menu_data_trigger
	  FOR INSERT OR DELETE on s_menu_data
	  compound TRIGGER
	/*
	  BEFORE STATEMENT IS -- 语句执行前触发(表级)
	  BEGIN
	    DBMS_OUTPUT.put_line('1、BEFORE STATEMENT .');
	  END BEFORE STATEMENT;
	
	  BEFORE EACH ROW IS -- 语句执行前触发(行级)
	  BEGIN
	    DBMS_OUTPUT.put_line('2、BEFORE EACH ROW .');
	  END BEFORE EACH ROW;
	
	  AFTER EACH ROW IS -- 语句执行后触发(行级)
	  BEGIN
	    DBMS_OUTPUT.put_line('3、AFTER EACH ROW .');
	  END AFTER EACH ROW;
	*/
	  AFTER STATEMENT IS -- 语句执行后触发(表级)
	  BEGIN
	    DBMS_OUTPUT.put_line('4、AFTER STATEMENT .');
	    reset_role_data;
	  END AFTER STATEMENT;
	
	end s_menu_data_trigger;
  • 角色s_role_menu功能权限变更时触发器,角色功能权限变化时触发
	create or replace trigger s_role_menu_trigger
	  FOR INSERT OR DELETE on s_role_menu
	  compound TRIGGER
	
	/*
	  BEFORE STATEMENT IS -- 语句执行前触发(表级)
	  BEGIN
	    DBMS_OUTPUT.put_line('1、BEFORE STATEMENT .');
	  END BEFORE STATEMENT;
	
	  BEFORE EACH ROW IS -- 语句执行前触发(行级)
	  BEGIN
	    DBMS_OUTPUT.put_line('2、BEFORE EACH ROW .');
	  END BEFORE EACH ROW;
	
	  AFTER EACH ROW IS -- 语句执行后触发(行级)
	  BEGIN
	    DBMS_OUTPUT.put_line('3、AFTER EACH ROW .');
	  END AFTER EACH ROW;
	*/
	
	  AFTER STATEMENT IS -- 语句执行后触发(表级)
	  BEGIN
	    DBMS_OUTPUT.put_line('4、AFTER STATEMENT .');
	    reset_role_data;
	  END AFTER STATEMENT;
	
	end s_role_menu_trigger;

1.6 用户-数据权限

  • 用户-角色关系视图
create or replace view user_role_v as
select r.p_id as r_id,r.s_name ,s.pf_user,u.p_id as u_id,
  decode(length(s.pf_role),4,1,0) as b_yn
 from s_role r
cross join s_user u --先做一个用户与角色的笛卡尔交叉,再连接用户角色表
left join s_user_role s on s.pf_user = u.p_id and s.pf_role = r.p_id
order by u.p_id,r.p_id
;
comment on table USER_ROLE_V is '用户角色勾选';

在这里插入图片描述

  • 用户-数据视图
create or replace view user_data_v as
select distinct u.u_id, u.r_id, --多角色同一个数据对象,用户的数据权限用 or 连接
       d.pf_data, d.s_where
  from user_role_v u --用户-角色关系视图
  left join s_role_data d -- 角色-数据表
    on d.pf_role = u.r_id
 where d.pf_data is not null and u.pf_user is not null
 ;
 
 comment on table USER_data_V is '用户-数据权限';

1.7 用户-数据权限字符串生成函数

根据用 用户 userid 和 数据对象 dataid 查询user_data_v 视图生成数据权限字符串,此字符串作为 sql 语句的 Where的一部分。

CREATE OR REPLACE FUNCTION GET_DATA_WHERE (userid varchar2,dataid varchar2) RETURN varchar2 is
  -- 获取userid的dataid的数据权限Where字符串
  where_str varchar2(512) := ' ';
BEGIN

  for curs in (select s_where,r_id from user_data_v where u_id = userid and pf_data = upper(dataid) ) loop
    --DBMS_OUTPUT.PUT_LINE(curs.r_id || ':' || curs.s_where);
    -- 循环组合where字符串,用 or 连接
    if curs.s_where is null then
      --定义了数据对象,权限where为空的,用 1=1 表示没有数据权限控制
       where_str := ' ';
    else
       where_str := where_str || curs.s_where || ' or ';
    end if;
  end loop;

  if instr(where_str,'or') > 5  then
    where_str := substr(where_str,1,length(where_str) - 3); --去除最后的 or
  else
    --没有定义的数据对象(游标返回行数为0)返回null
    where_str := null;
  end if;
  
  return where_str;
END GET_DATA_WHERE;

2 数据权限的后端实现

基于以上的用户数据权限设计,后端验证 token 时返回用户的帐号 userid ,根据用户的 userid 和 数据对象 dataid 显示数据的访问范围。

2.1 创建数据权限函数 getDataWhere

// 获取用户 userid 的数据对象 dataid 的数据权限 where 字符串
func getDataWhere(userid string, dataid string) string {
	dataID := strings.ToUpper(dataid) // 数据对象名改为大写
	sSql := "select get_data_where('" + userid + "','" + dataID + "') as s_where from dual"
	result := Icrud.SelectData(sSql)

	var swhere []map[string]string
	err := json.Unmarshal([]byte(result), &swhere)
	if err != nil {
		panic(err)
	}
	//fmt.Println(swhere)
	s_where := swhere[0]["S_WHERE"]

	return s_where
}

2.2 重构token验证函数

增加返回 token 里的 userid

// token 验证函数
func vToken(w http.ResponseWriter, tokenString string) (string, int) {
	tokenmap := make(map[string]interface{})
	tokenmap = token.ValidateTokenHandler(w, tokenString)
	if tokenmap["status"] != http.StatusOK {
		// 抛出错误信息
		httpResWriter(w, tokenmap)
		return "", 401
	}
	// 返回userid 和 200
	return tokenmap["userid"].(string), 200
}

2.3 Token验证时保存Userid

REST请求时验证token,代码片段:

switch req["RESTorSQL"] {
	case "REST":
		// 是否配置了需要token验证
		if config.Conf.OpenToken {
			// token有效性验证
			tokenString := req["Authorization"].(string)
			userid, status := vToken(w, tokenString)

			if status != 200 {
				return
			}
			req["Userid"] = userid //token里的userid
		}

2.4 Where 条件增加数据权限

// 根据 userid 和 数据对象 加数据权限,数据库函数 get_data_where实现,返回的where 用 and 连接
	if config.Conf.OpenToken {
		userid := req["Userid"].(string)

		dataWhere := getDataWhere(userid, resName) // 数据权限 where
		urlWhere := qry["Where"]                   // url 上的 where
		if len(dataWhere) > 3 {
			if len(urlWhere) > 3 {
				// 如果有数据权限约束就加上
				qry["Where"] = qry["Where"] + " and (" + dataWhere + ")"
			} else {
				qry["Where"] = dataWhere
			}
		}
	}

3 前端实现

3.1 页面-数据对象设置

创建一个页面-数据对象设置页面即可。这个需要开发人员来设置,只有开发人员才知道页面用到那些数据,当然不进行数据权限约束的数据对象是不需要设置的。
在这里插入图片描述

3.2 角色-数据对象权限设置

创建角色-数据对象权限设置页面即可。就是定义sql查询语句的where条件,这个也由开发人员设置。
在这里插入图片描述

3.3 用户-数据权限查询

创建查询用户的数据权限页面
在这里插入图片描述

4 实操演练

4.1 设置页面-数据对象

在这里插入图片描述

4.2 设置角色-数据权限

  1. 新建一个角色 YSSH
  2. 设置功能权限
    在这里插入图片描述
  3. 设置角色-数据权限
    在这里插入图片描述

4.3 查询用户-数据权限

  1. 设置用户-角色 在这里插入图片描述
  2. 查询用户-数据权限
    在这里插入图片描述

4.4 用户登录

在这里插入图片描述

4.5 企业管输需求

在这里插入图片描述
如上图,列表只显示本企业的数据,企业ID的列选也只有本企业可选了。
后台的SQL查询语句类似如下:

select *
  from dd_xuqiu
 where to_char(date_t, 'YYYY-MM-DD') >= '2022-07-10' 
   and qy_id = nvl('', qy_id)
   and yz_id = nvl('', yz_id)
   and (qy_id = 'Q0010')

其中 qy_id = ‘Q0010’ 就是权限控制条件。

5 跋尾

此数据权限基于管输管理平台设计,管输平台主要角色有总部、企业、油库、港口、货代等,不用角色的数据权限都不同,还有很大差别,此设计完全满足数据权限的管理。如果参与的主体很多年的话,可能会有角色组合爆炸。
对于有层级关系的数据也基本能实现数据权限的控制。不需要构建组织机构表,但要用角色实现组织机构关系。


本文完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/630965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT状态机10-QKeyEventTransition和QMouseEventTransition的使用

1、QMouseEventTransition的使用 首先明白 QMouseEventTransition 继承自 QEventTransition类。 关于QEventTransition类的使用,可参考 QT状态机9-QEventTransition和QSignalTransition的使用 回顾 QT状态机9-QEventTransition和QSignalTransition的使用 中的状态切换代码,如…

C++动态内存区域划分、new、delete关键字、泛型编程、函数模版、类模版

目录 一、C/C中程序的内存区域划分 为什么会存在内存区域划分? 二、new关键字 1、内置类型的new/delete使用方法: 2、new和delete的本质 3、常见面试题——malloc/free和new/delete的区别 三、模版 1、泛型编程 2、函数模版 (1&…

【C语言】6.C语言VS实用调试技巧(2)

文章目录 6.调试举例17.调试举例28.调试举例3&#xff1a;数组9.编程常⻅错误归类9.1 编译型错误9.2 链接型错误9.3 运⾏时错误 – 6.调试举例1 求 1!2!3!4!…10! 的和。 int main() {int n 0;int i 0;int ret 1;int sum 0;for (n 1; n < 3; n) {for (i 1; i < …

LBSS138LT1G 丝印J1 SOT-23 N沟道 50V/200mA 贴片MOSFET

LBSS138LT1G的应用领域广泛&#xff0c;主要因为它是一种N沟道金属氧化物半导体场效应晶体管&#xff08;MOSFET&#xff09;&#xff0c;具有低电荷、快速开关速度和高阻断特性。以下是一些典型的应用领域&#xff1a; 1. 消费电子产品&#xff1a;LBSS138LT1G常用于电视、音响…

四川景源畅信:如何更好的为抖音小店做引流?

在数字化营销的浪潮中&#xff0c;抖音小店作为新兴的电商形态&#xff0c;正以其独特的社交属性和流量优势吸引着众多商家的目光。如何为抖音小店引流&#xff0c;成为许多店主心中的疑问。本文将深入探讨有效提升店铺流量的策略&#xff0c;助你在抖音平台上快速崛起。 一、内…

【漏洞复现】Secnet-智能路由系统 actpt_5g.data信息泄露

0x01 产品简介 Secnet安网智能AC管理系统是广州安网通信技术有限公司(简称“安网通信”)的无线AP管理系统 0x02 漏洞描述 Secnet智能路由系统 acipt 5g.data 接口存在信息泄露漏洞&#xff0c;未经身份验证的远程攻击者可以利用此漏洞获取系统账户名密码等重要凭据&#xff…

印染工厂5G智能制造数字孪生可视化平台,推进行业数字化转型

印染工厂5G智能制造数字孪生可视化平台&#xff0c;推进行业数字化转型。印染工厂正迈入一个全新的时代&#xff0c;这个时代以5G智能制造数字孪生可视化平台为核心&#xff0c;推动整个行业的数字化转型。不仅是一场技术革命&#xff0c;更是一次产业变革&#xff0c;为印染工…

在微信小程序项目中安装和使用 Vant 组件库

vant Wwapp 小程序开发组件库官网 Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装 Vant 组件库 1.在微信小程序项目文件目录的空白位置右键&#xff0c;选择在外部终端窗口中打开 2在命令行输入如下命令&#xff08;在项目中创建包管理配置文件 package.json&#xff09; …

AI预测体彩排3采取878定位大底=23策略+杀断组+杀组选+杀和尾+杀和值012缩水测试5月15日预测第1弹

昨天与一位玩排3的彩友通过视频直播的形式聊了下&#xff0c;受益匪浅&#xff0c;给我提供了一些比较有价值的建议&#xff0c;比如&#xff0c;对于878的定位策略&#xff0c;方向是没问题的&#xff0c;但是8783的话&#xff0c;还是缺乏一定的命中率&#xff0c;如果87823&…

【算法学习】拓扑排序

文章目录 拓扑排序课程表 拓扑排序 算法原理&#xff1a; 1.先找出图中入度为0的点&#xff0c;将该点加入到队列中 2.队列不为空时&#xff0c;拿出队头元素加入到最终结果 3.再遍历该点的邻接阵&#xff0c;将连接该点的点的入度全部减减 4.判断减减的点是否为入度为0&#…

亚马逊跨境电商平台优势凸显,武汉星起航解析助力卖家把握商机

在全球电商市场的激烈竞争中&#xff0c;亚马逊凭借其独特的优势和卓越的运营能力&#xff0c;成为众多卖家首选的跨境电商平台。武汉星起航作为深耕亚马逊跨境电商领域的领军企业&#xff0c;对亚马逊平台的优势有着深刻的理解和独到的见解。本文将重点探讨亚马逊跨境电商平台…

降Compose十八掌之『亢龙有悔』

公众号「稀有猿诉」 原文链接 降Compose十八掌之『亢龙有悔』 Jetpack Compose是新一代的声明式的UI开发框架&#xff0c;由Google在2019年推出&#xff0c;最初是作为Android的新式UI开发框架&#xff0c;但它本质是一个声明式UI开发框架&#xff0c;并不受制于底层的平…

腾讯云环境安装单机版minio

Minio 下载安装 wget https://dl.min.io/server/minio/release/linux-amd64/minio修改minio 文件为可执行文件 chmod x minio3、启动&#xff0c;随机端口启动 ./minio server /data/miniodata # 或者指定密码执行 MINIO_ACCESS_KEYmyminioadmin MINIO_SECRET_KEYmyminioadm…

精酿啤酒:品质的保障与消费者的信赖

在啤酒市场中&#xff0c;Fendi club啤酒以其卓着的品质和消费者的信赖赢得了广泛的认可。作为精酿啤酒的品牌&#xff0c;Fendi club啤酒始终坚持对品质的严格把控&#xff0c;为消费者带来放心的口感体验。 品质的保障源于Fendi club啤酒对原料的严谨挑选和加工。他们深知&a…

在大型项目上,Python 是个烂语言吗?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Python的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; python项目超过5万行&#x…

STM32的FLASH学习笔记

不同型号的 STM32&#xff0c;其 FLASH 容量也有所不同&#xff0c;最小的只有 16K 字节&#xff0c;最大的则达到了1024K 字节。大容量产品的闪存模块组织如图所示&#xff1a; STM32 的闪存模块由&#xff1a;主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。 ​ ①主…

pikachu靶场通关之暴力破解token防爆破

这里写pikachu靶场token防爆破的第二种解法 用python脚本跑&#xff0c;下面是代码 import requests from bs4 import BeautifulSoup# url填自己的url url http://localhost:8086/pikachu-master/vul/burteforce/bf_token.php # 取出账号字典里的值&#xff0c;1.txt就是账号…

docker安装minio附带图片

1.拉镜像 docker pull minio/minio 2.创建挂载点目录 mkdir -p /usr/local/minio/config mkdir -p /usr/local/minio/data 3.创建minio容器 docker run \ -p 19000:9000 \ -p 9090:9090 \ --nethost \ --name minio \ -d --restartalways \ -e "MINIO_ACCESS_KEYmini…

k8s 二进制安装 优化架构之 部署负载均衡,加入master02

目录 一 实验环境 二 部署 CoreDNS 1&#xff0c;所有node加载coredns.tar 镜像 2&#xff0c;在 master01 节点部署 CoreDNS 3&#xff0c; DNS 解析测试 4&#xff0c; 报错分析 5&#xff0c;重新 DNS 解析测试 三 master02 节点部署 1&#xff0…

[HNCTF 2024] crypto/pwn

周日的比赛&#xff0c;赛后拿别人的WP又作了俩&#xff0c;最后一个题也是没弄懂&#xff0c;先记一下吧。 Crypto EZmath 一个简单的函数题。在sagemath里有个two_squares函数&#xff0c;可以从平方和恢复两个规模相近的数。这种比较适合于RSA里的p,q。另外未知的e用来猜…