背景说明
最近写代码的时候又开始接触控制台应用了。我接触的项目都是历史悠久的,所有的权限管理都不是很得心应手。
于是我就想着能不能从零开始设计一个,整理一下思路。当然,我实际使用与否并不重要。
权限管理主要是为了安全,项目中的权限管理全部控制在前端,非常不安全。
前端防君子,不防小人。
Cage.jpg
当然这个造轮主要是为了造一个你满意的正确的控制框架,所以设计采用MVP模式,循序渐进的开发。
让我们来了解一下权限控制的设计和实现。
如果生产想直接使用它,也建议目前成熟的框架:
春天-安全
春天安全-01-你好世界
希罗
希罗
权限设计基础知识
在写代码之前,我们先学习一些权限设计的基础知识。
到目前为止,网上的信息还是比较丰富的,所以我们选取了一些经典的内容。
用户权限模型
对于权限管理,最简单的想法应该是给每个用户分配不同的权限。
输入图片描述。
但是这种设计有一个很大的问题就是不够灵活,用户多的时候很难维护。
RBAC0模型
于是,很多人想到了在用户和权限之间加一个角色。
到目前为止,它是最流行的权限设计模型,基于角色的访问控制。
输入图片描述。
这里有三个主要概念:用户、角色和权限。
其实用户才是我们需要控制的主体。
角色是桥梁,也是这个模式中最巧妙的地方。就像我们每个人一样,都是父母的孩子,孩子的父母,公司的员工。我们在不同的时间扮演不同的角色。不同的角色,对应不同的权限。
许可是一个相对宽泛的概念。对于控制台,可能是菜单权限、资源权限、增加、删除、检查数据的操作权限。
在数据库表的设计中,实际上有三个实体表:
用户表
角色表
特权许可表
然后是两个映射表:
User_role用户-角色关系表
角色权限角色权限关系表
以上是RBAC的核心设计和模型分析,也叫RBAC0。基于核心概念,RBAC还提供了一个扩展模型。
输入图片描述。
包括RBAC1、RBAC2和RBAC3模型。
这三种类型描述如下。
RBAC1模型
本文主要介绍等级角色的概念。
输入图片描述。
这种设计可以对角色进行分组和分层,在一定程度上简化了权限管理。
RBAC2模型
在核心模型的基础上进行角色约束控制,在RBAC2模型中加入职责分离关系,规定了在将权限赋予角色时,或者将角色赋予用户时,以及用户在某一时刻激活角色时,应遵循的强制性规则。
然而,在实践中,这种模式并不常用。这里没有展开。
RBAC3模型
最通用的型号也是各大公司最常用的型号。
我们平时看到的权限管理,一般都是和公司的组织架构一一对应的。
即模型来源于生活。
用户组
最常见的概念是用户组。
比如我们常用的gitlab,加入一个项目团队,然后你就可以操作相关的代码仓库了。
就像我们在现实工作中加入一个项目组,是一个道理。这里的用户一般都是同行。
公司的一般组织有上下级关系。一般公司的组织结构图如下:
输入图片描述。
这种架构的优点是容易改变和调整权限,也容易控制权限。
在设计表格时,我们可以添加一个user_group表,将一些用户放在同一个组中。
位置
实际上,职位与角色的概念相对应,不同的职位有不同的权利,即使是在同一个项目中。
这就像一个项目负责人和普通开发的区别。
输入图片描述。
权限模型
最终的模型如下所示:
输入图片描述。
授权过程
赋予用户角色。这是如何实现的?
手动授权
最常见的是手工设计。
当项目初始化时,有一些基本的角色和管理员。
然后管理员配置相关权限。
批准授权
用户可以申请相应的角色,然后通过审批流程赋予相应的角色。
比如有时候我们可以申请访问某个资源,背后的道理也差不多。
其实在这里,只是一些基础知识。
想要设计一个好的菜单,需要考虑的东西还是很多的,比如菜单设计,页面设计,流程如何设计的更合理?可以自定义角色吗?用户可以自定义菜单吗?
等一下。但暂时不深入讨论这些。本文的重点是实现一个简单版本的权限控制框架。
传统RBAC的缺点
当然,以上模式还是有一些不足。
例如,正如我在一篇论文中读到的:
虽然RBAC已经得到了广泛的应用,但是传统的RBAC模型仍然存在不足,主要表现在以下两个方面:(1)访问控制不能满足实际应用的需要,Web应用系统需要更细粒度的访问控制。(2)该模型只定义了访问控制的内部机制,没有提出简单友好的访问控制实现。对于Web系统的用户来说,友好直观的用户界面是系统必不可少的一部分。
解决方法是设计一个操作码,然后用一个字符串来标识,比如10000可以代表可访问菜单,没有添加、删除、更改的权限。
当然这些都需要我们根据自己的业务来设计。
假设设计产品完成,数据库完成,如何灵活实现编码时的权限控制?
设计理念
基本思想是一种类似shiro的方式。
您可以指定角色或权限来访问固定菜单。
设计目标
(1)当然,我们希望能够更加灵活,具体到一个固定的方法,而不是拘泥于一个固定的控制器要求。
(2)容易写,后期可以通过评论指定。
(3)从简单到复杂,初期只实现基于权限编码的控制,暂时不考虑基于角色的控制。因为每个登录的用户都可以获得相应的角色和权限代码。
整个过程
用户页面请求获取用户登录信息。分布式系统可以基于 redis session 或者 JWT 等,获取当前用户的所有权限编码校验用户是否拥有请求的权限返回对应的页面结果
第一步-界面设计
核心接口
public?inte***ce?IPrivilege?{????/**?????*?是否拥有权限?????*?????*?{@code?true}?拥有?????*?{@code?false}?不拥有?????*?????*?@param?context?上下文?????*?@return?是否?????*?@since?0.0.1?????*/????boolean?hasPrivilege(final?IPrivilegeContext?context);}
这是一切的核心。我们只关心是否有权限,结果是一个布尔值。
那么语境的内容是什么呢?
public?inte***ce?IPrivilegeContext?{????/**?????*?拥有的权限上下文?????*?@return?拥有?????*?@since?0.0.1?????*/????IPrivilegeOwn?own();????/**?????*?需要的权限上下文?????*?@return?拥有?????*?@since?0.0.1?????*/????IPrivilegeAcquire?acquire();}
其实有两个接口,一个是拥有的权限,一个是需要的权限。
权限信息
两种权限的内容如下:
拥有的权限public?inte***ce?IPrivilegeOwn?{????/**?????*?拥有的权限编码列表?????*?@return?编码列表?????*?@since?0.0.1?????*/????List<IPrivilegeInfo>?ownPrivilege();}需要的权限public?inte***ce?IPrivilegeAcquire?{????/**?????*?需要的权限编码列表?????*?@return?编码列表?????*?@since?0.0.1?????*/????List<IPrivilegeInfo>?acquirePrivilege();}
步骤2-编程实现
***ven介绍了
<dependency>????<group>com.github.houbb</group>????<artifact>privilege-core</artifact>????<version>${最新版本}</version></dependency>
用户定义的权限
实现IPrivilegeOwn接口。
可以查询数据库、文件等。
public?class?PrivilegeOwnOne?implements?IPrivilegeOwn?{????@Override????public?List<IPrivilegeInfo>?ownPrivilege()?{????????IPrivilegeInfo?info?=?PrivilegeInfo.newInstance().code("001");????????return?Collections.singletonList(info);????}}
自定义所需的权限
public?class?PrivilegeAcquireTwo?implements?IPrivilegeAcquire?{????@Override????public?List<IPrivilegeInfo>?acquirePrivilege()?{????????IPrivilegeInfo?one?=?PrivilegeInfo.newInstance().code("001");????????IPrivilegeInfo?two?=?PrivilegeInfo.newInstance().code("002");????????return?Arrays.asList(one,?two);????}}
测试1
IPrivilegeOwn?own?=?new?PrivilegeOwnOne();IPrivilegeAcquire?acquire?=?new?PrivilegeAcquireTwo();boolean?hasPrivilege?=?PrivilegeBs.newInstance()????????.own(own)????????.acquire(acquire)????????.hasPrivilege();Assert.assertFalse(hasPrivilege);
我们这里得到的结果是没有权限,因为默认的策略是所有指定的代码都被拥有,然后就通过了。
内置策略
内置策略,可以直接通过特权获得。
策略
声明
all()需要权限代码,只有当它们全部拥有时,才能被认为拥有权限。(默认策略)any()需要权限代码。如果你有任何一个,你就被认为是有权限的。Allow()白名单,直接返回所有权权限。Deny()黑名单,直接返回无权限。
测试2
我们指定如果有就可以通过。
IPrivilegeOwn?own?=?new?PrivilegeOwnOne();IPrivilegeAcquire?acquire?=?new?PrivilegeAcquireTwo();IPrivilege?privilege?=?Privileges.any();boolean?hasPrivilege?=?PrivilegeBs.newInstance()????????.own(own)????????.acquire(acquire)????????.privilege(privilege)????????.hasPrivilege();Assert.assertTrue(hasPrivilege);
步骤3-带注释的编程
注释的必要性
当然上面已经实现了最基本的实现,但是实际使用一定不能由开发者手动指定。
那会有多麻烦。
我们的叙事设计可以基于注释的实现。
批注定义
@Inherited@Documented@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public?@inte***ce?PrivilegeAcquire?{????/**?????*?权限编码?????*?@return?编码?????*?@since?0.0.4?????*/????String[]?code()?default?{};}
这个注释可以放在方法上,以指定访问所需的编码。
***ven介绍了
通常,我们在spring-mvc项目中使用它。
<dependency>????<groupId>com.github.houbb</groupId>????<artifactId>privilege-mvc</artifactId>????<version>${最新版本}</version></dependency>
自定义侦听器
我们定义自己的***来处理登录用户的权限。
@Componentpublic?class?MyPrivilegeInterceptor?extends?AbstractPrivilegeInterceptor?{????@Override????protected?void?fillSubject(ISubject?subject,?HttpServletRequest?request)?{????????String?id?=?request.getParameter("id");????????if("admin".equals(id))?{????????????subject.privileges("1001");????????}????}}
我们只对admin用户设置权限码为1001,其他用户不会处理。
注册***
注册我们的***,访问/hello时生效。当然可以根据实际情况进行调整。
@Configurationpublic?class?InterceptorConfig?extends?WebMvcConfigurerAdapter?{????@Autowired????private?MyPrivilegeInterceptor?myPrivilegeInterceptor;????@Override????public?void?addInterceptors(InterceptorRegistry?registry)?{????????registry.addInterceptor(myPrivilegeInterceptor)????????????.addPathPatterns("/hello");????}}
业务代码
控制器
当我们通过注释@PrivilegeAcquire指定访问当前方法时,我们需要权限代码1001。
@Controllerpublic?class?HelloController?{????@RequestMapping("/hello")????@ResponseBody????@PrivilegeAcquire(code?=?"1001")????public?String?hello()?{????????return?"hello";????}}
应用程序
启动类定义如下:
我们通过注释@EnablePrivilege开始权限检查。
@SpringBootApplication@EnablePrivilegepublic?class?Application?{????public?static?void?***in(String[]?args)?{????????SpringApplication.run(Application.class,?args);????}}
测试
权限不足,无法访问http://localhost:8080/hello;
There?was?an?unexpected?error?(type=Internal?Server?Error,?status=500).Has?no?privilege!?Acquire:?<[1001]>,?Own:?<[]>
去http://localhost:8080/hello?Id =管理员正常访问。
摘要
这里完成了最简单的权限设计。
其实在设计的时候,为了适应各种场景,项目有很多模块:
(1)1)API核心的接口和注释定义
(2)2)核心编程的基本实现可以脱离spring。
(3)代理与spring可以直接使用的代理分离。
(4)spring结合AOP模式的spring
(5)mvc结合spring——MVC也是最常用的模式。
未来,我们将继续优化和添加对springboot的支持。
也可以考虑学习shiro的精髓,引入更多的功能。
开放源地址
https://github.com/houbb/privilege
欢迎叉/星~~
愿景
该框架可以支持更多的功能,如基于角色和基于用户。
开箱即用的设计,比如最基础的sql+mybatis通用权限管理框架,类似于quartz。
一个完整的项目设计,支持前后页。
单独服务打磨可以对外提供认证服务,结合auth等进一步优化权限管理。
本文来自西狸森溪寒风投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/647918.html