当前位置: 首页 > Web前端 > HTML

关于antdesignpro的权限方案设计

时间:2023-03-28 00:49:24 HTML

访问控制(Accesscontrol)是指访问者对受保护资源的访问操作的控制和管理。控制管理确保授权的人可以访问受保护的资源,而未授权的人不能访问受保护的资源。现实生活中的访问控制可以通过支付或者认证来实现。例如:去电影院看电影,需要买电影票,否则验票员是不会让你进去的。门禁控制的模型有很多,比如:全权访问控制模型(DiscretionaryAccessControl)强制访问控制模型(MAC:MandatoryAccessControl)角色访问控制模型(RBAC:Role-basedAccessControl)属性访问控制模型(ABAC:Attribute-BasedAccessControl)DAC自主访问控制(DAC:DiscretionaryAccessControl),系统会识别用户,然后根据访问对象的访问控制列表(ACL:AccessControlList)或访问控制矩阵(ACL:AccessControlMatrix)信息来确定用户可以对其进行哪些操作,比如读或调整。拥有对象权限的用户可以将对象权限分配给其他用户,因此称为“全权委托”控制。自主访问控制模型是一种相对宽松但有效的保护资源不被非法访问和使用的手段。说松是因为它是自主控制的,在保护资源的时候是以个人意志为转移的;之所以说有效,是因为它能清楚明确地指出主体是在访问还是在使用客体。实现什么样的权限,任何超出指定权限的访问行为,经过访问控制列表的判断,都会被阻止。一个比较典型的场景是在Linux文件系统中:系统中的每一个文件(有些特殊文件可能没有,比如块设备文件等)都有一个所有者。文件的所有者是创建该文件的计算机的用户(或事件,或另一个文件)。那么这个文件的自主访问控制权限就由它的创建者如何设置和分配来决定了。文件的拥有者拥有访问权限,可以为自己和其他用户分配访问权限只有被标记为受访问控制的用户(或其他主体)和文件(或其他对象)被标记为固定的安全属性(如安全级别、访问权限等),当每次访问发生时,系统都会检查安全性属性来确定用户是否具有访问文件的权限。MAC最早主要用于军事应用,通常与DAC结合使用。将两种访问控制机制的过滤结果累加起来,以达到更好的访问控制效果。也就是说,主体只有通过DAC限制检查和MAC限制检查的双重过滤装置,才能真正访问客体。一方面,用户可以使用DAC来防止其他用户攻击那些属于自己的对象;另一方面,由于用户不能直接改变MAC属性,MAC提供了一个不可逾越的更强大的安全保护层。防止其他用户意外或故意滥用DAC。RBAC角色访问控制(RBAC:Role-basedAccessControl),各种权限不是直接授予具体的用户,而是在用户集和权限集之间建立一个角色集。每个角色对应一组相应的权限。一旦为用户分配了合适的角色,该用户就拥有该角色的所有操作权限。目前基于角色的访问控制模型被广泛应用,尤其是在2B方向的SAAS领域。这就是我们今天要关注的。RBAC虽然简化了权限的管理,但是对于复杂场景下的角色管理仍然不够灵活。例如,主体与客体之间的权限复杂多变,可能需要维护大量的角色及其授权关系;新对象还需要处理所有相关角色。基于属性的角色访问控制就是为了解决这个问题而设计的。ABAC属性访问控制(Attributes-basedAccessControl)是一种非常灵活的访问控制模型。属性包括请求体的属性、请求对象的属性、请求上下文的属性、操作的属性等。比如老张是班主任(主体的属性),上课的时候可以踢(操作的属性)普通学生的小明(客体的属性)(上下文的属性)。可见,只要对属性进行精确的定义和划分,ABAC就可以实现非常复杂的权限控制。例如:大二(年级)会计(专业)二班(班)班长(职务)可以在校内网(环境)上传(操作)班级照片。但是由于ABAC的复杂性,对于现在的SAAS领域来说似乎有点大材小用,所以在SAAS领域使用ABAC的平台很少,目前一些云服务使用ABAC的比较多。数据栈中的RBAC我们产品使用的是RBAC权限方案,所以目前只分析RBAC。RBAC是角色访问控制,所以我们首先要知道的是用户的角色。对此,我们的项目中有用户管理和角色管理两个模块。用户管理在uic的用户管理中提供了创建、编辑、删除用户账号等功能。在数据栈产品中,有租户,每个租户下还有一个用户管理,用于管理租户内的用户。能够设置当前用户的角色,包括租户所有者、项目所有者、项目经理等。角色管理在角色管理中,您可以看到角色的定义及其拥有的访问权限。通过用户管理和角色管理中的用户定义,我们可以获得当前用户完整的产品访问权限。当用户进入某个功能时,我们可以通过对比当前的访问权限和用户的访问权限,进而得出准入的结论。对于我们前端开发者来说,我们需要的其实是获取一个用户的角色权限来比较获取到的权限,并对结果进行不同的处理。我们来看看antdesignpro的权限方案是怎么处理的。antdesignpro中的权限方案,业界比较常见的antdesignpro中的权限方案是如何设计的?在获取用户角色权限之初,进入页面时会进行登录验证。如果没有登录,会跳转到登录页面,进行登录操作。登录成功后,会通过setAuthority方法将当前用户的角色数据保存到localStorage中,方便我们重新进入页面时获取。通过登录验证的,直接进入项目,渲染页面基本布局的BasicLayout组件。在BasicLayout组件中,我们使用Authorized组件。Authorized挂载时,会触发renderAuthorize给CURRENT赋值。后续的权限检查会使用CURRENT,这个比较关键。renderAuthorize方法是一个curried函数,当使用getAuthority在内部获取角色数据时,它会为CURRENT赋值。让当前:字符串|string[]='NULL';类型CurrentAuthorityType=string|字符串[]|(()=>typeofCURRENT);/***使用权限或getAuthority*@param{string|()=>String}currentAuthority*/constrenderAuthorize=(Authorized:any)=>(currentAuthority:CurrentAuthorityType)=>{if(currentAuthority){if(typeofcurrentAuthority==='function'){CURRENT=currentAuthority();}if(Object.prototype.toString.call(currentAuthority)==='[objectString]'||Array.isArray(currentAuthority)){CURRENT=currentAuthorityasstring[];}}else{CURRENT='NULL';}returnAuthorized;};export{CURRENT};exportdefault(Authorized:any)=>renderAuthorize(Authorized);到这里,项目的权限获得以及更新就完成了。下一步是验证权限。权限验证需要以下环境参数:authority:当前访问权限为访问权限currentAuthority:当前用户的角色,即CURRENTtarget:验证成功后显示的组件Exception:验证失败的组件verified与Authorizedcomponents结合用于需要权限验证的组件。Authorized组件内部,实现了checkPermissions方法,验证当前用户角色是否有访问权限。如果有权限,则直接显示当前组件,如果没有,则显示无权限等信息。授权组件的实际类型IAuthorizedType=React.FunctionComponent&{Secured:typeofSecured;检查:检查类型;AuthorizedRoute:typeofAuthorizedRoute;};constAuthorized:React.FunctionComponent=({children,authority,noMatch=(),})=>{constchildrenRender:React.ReactNode=typeofchildren==='undefined'?空:儿童;constdom=check(authority,childrenRender,noMatch);return<>{dom};};functioncheck(权限:IAuthorityType,目标:T,异常:K):T|K|React.ReactNode{returncheckPermissions(authority,CURRENT,target,Exception);}/***通用权限检查方法*常用权限检查方法*@param{权限判断|权限判断}authority*@param{你的权限|你的权限描述}currentAuthority*@param{传递的组件|传递组件}target*@param{失败的组件|没有通过组件}Exception*/constcheckPermissions=(authority:IAuthorityType,currentAuthority:string|string[],target:T,Exception:K,):T|K|React.ReactNode=>{//没有判断权限。默认查看全部//退权限,返回目标;如果(!authority){返回目标;}//数组处理}}elseif(authority.includes(currentAuthority)){返回目标;}返回异常;}//字符串处理if(typeofauthority==='string'){if(Array.isArray(currentAuthority)){if(currentAuthority.some((item)=>authority===item)){}}elseif(authority===currentAuthority){返回目标;}返回异常;}//Promise处理if(authicknessinstanceofPromise){returnok={target}error={Exception}promise={authority}/>;}//函数处理if(typeofauthority==='function'){constbool=authority(currentAuthority);//函数执行后返回值为Promiseif(boolinstanceofPromise){returnok={target}error={Exception}promise={bool}/>;}if(bool){返回目标;}返回异常;}thrownewError('不支持的参数');};在页面上使用授权组件非常方便。对于需要权限控制的组件,使用Authorized组件结合CanfunctionNoMatch=()=>{return

404
}{children}/Authorized>我们也可以使用路由来匹配组件,这里是使用v4,不管组件写在哪里,只要匹配上,子组件就会渲染到那里。}/>}>(Component?:render(props))}/>我们的权限方案我们目前的权限有两种方案,一种是离线质量等产品使用的旧权限方案,另一种是资产标签使用的新权限方案。老权限方案老方案中采用的是通过界面获取数据的方式,但是获取的数据只到菜单级别。获取到的数据会保存在缓存中,方便我们的业务包和子产品使用。监听业务包中页面地址的变化,判断是否有权限进入当前页面,并根据结果进行相应的处理,其实就是一个路由守卫的作用。在子产品中,根据数据判断是否显示当前菜单项。这两个结合起来形成了我们的旧方案。随着数据栈的增长,老方案逐渐暴露出很多问题。权限控制范围太小,我们只控制菜单的层级,对于特殊页面和某些需要控制功能的场景(如:编辑、添加、删除等),目前只控制后台界面有限制,页面没有限制。如果需要实现这个功能,需要增加额外的接口和处理逻辑。我们把权限的处理分为两部分,业务包和子产品,但是两者之间的耦合度很高,经常改变一个地方,其他地方也需要相应地改变。在业务包中,我们控制了各个产品的页面访问逻辑。每当我们需要添加菜单时,我们都需要添加相应的菜单处理逻辑。当我们添加一个商品时,我们需要添加这个商品对应的所有菜单逻辑。目前堆栈的子产品数量已经超过10+,可想而知这部分的处理逻辑是多么的臃肿。实际问题不止以上三点,但这三点足以让我们去探索新的权限方案。新的权限方案新方案中,业务包只保留权限的公共方法,页面权限判断逻辑去中心化。子产品维护自己的权限判断逻辑。修改权限的逻辑也很容易。相比Antdesignpro是按角色判断的。在新的方案中,我们将角色权限的判断逻辑交给了后端,后端经过相应的处理后返回相应的代码集。对于每个需要设置访问权限的模块,我们定义一个代码code来比较在后端返回的集合中是否可以找到相同的代码。如果能找到描述,则有访问当前模块的权限,否则没有。这样做之后,我们只需要关心是否可以进入即可。当获取到权限点时,也会根据权限点缓存有权限访问的路由列表。当路由发生变化时,可以在权威路由列表中查找。如果没有找到,它会重定向等等。运行,也就是路由守卫的功能。总结经过上面的介绍,我们已经了解了权限方案,主要分为两个阶段:获取权限阶段:在获取权限阶段,往往是在用户登录或者进入项目时,第一时间获取相应的用户信息。权限验证权限阶段:将用户的权限与当前模块的访问权限进行比较。根据结果??操作知道这些后,就可以根据自己的场景制定相应的权限方案了。