news 2026/10/24 9:23:16

RBAC权限模型全解析:从表结构设计到后端接口校验落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RBAC权限模型全解析:从表结构设计到后端接口校验落地

先说一个我实际参与过的场景。某企业内部管理后台重构之前,权限一直靠前端路由“藏菜单”实现,后端接口基本裸奔。上线一个月后发现普通员工能直接调用财务模块的导出接口,数据列表虽然没有入口,但抓包就能拿到。后来我在重构方案里完整落地了RBAC模型,把权限体系从“界面隐藏”升级成“后端强制校验”,这种问题才彻底堵住。

RBAC(Role-Based Access Control,基于角色的访问控制)是整个权限控制领域最经典、也最经得起实践检验的一套模型。核心思想概括成一句话就是:权限不直接分配给用户,而是分配给角色;用户通过拥有某些角色,间接获得对应的权限。这套模型在后台管理系统、SaaS平台、企业内部应用里几乎是标配。这篇内容适合正在设计权限模块的后端开发、刚接触企业级系统的初级工程师,以及准备把权限从“硬编码判断”升级成“可配置管理”的团队参考。

下面我会按照一条完整的落地链路来拆解:从模型选型、表结构设计、后端拦截实现,到缓存策略和常见坑位。代码以Java风格为主,但思路完全能迁移到其他技术栈。

1. RBAC到底在解决什么问题

1.1 先从一段权限失控事故说起

我接手过一个已经运行两年的老系统,当时权限模块的状态是:用户表里有一个“角色名称”字段,角色名称是字符串,比如“admin”“editor”“viewer”。代码里到处是这样写的:

if ("admin".equals(user.getRoleName())) { // 允许导出 }

这套方案初期看着简单,运行一年后就开始出问题。

第一个问题:角色变多了。系统里出现了“部门管理员”“项目管理员”“财务专员”“运营实习生”等十几种角色,每种角色能做的事都不一样,继续用字符串判断,代码里全是散落的魔法值,改一个权限要翻遍全项目。

第二个问题:需求方经常说“这个角色应该多一个功能”“那个角色不应该看到这个页面”。每来一次需求,就要改一次业务代码,重新发布一次系统。

第三个问题也是最严重的:权限校验只发生在菜单显示这一层,后端接口本身没有强制校验。只要有人通过接口文档或者抓包工具拿到接口地址,就能绕过前端直接调用。那次财务接口泄露事故,就是有人用普通账号调了导出接口,把全量数据下载走了。

权限失控的本质,是“用户”和“权限”之间缺少一层缓冲。如果把权限直接绑在用户身上,一旦用户数量变大、角色类型变多,系统就成了一团解不开的线。RBAC模型解决的就是这个问题:在用户和权限之间引入“角色”这个中间层,让二者解耦。

1.2 RBAC的核心思想与三个基本元素

RBAC模型最核心的三个元素是用户(User)、角色(Role)、权限(Permission)。三者之间的关系非常简洁:

  • 用户和角色是多对多关系。一个用户可以有多个角色,一个角色可以分配给多个用户。
  • 角色和权限是多对多关系。一个角色拥有多个权限,一个权限可以属于多个角色。
  • 用户不直接拥有权限,而是通过“拥有角色”间接获得权限。

这个设计带来的直接好处是:调整某个角色拥有的权限,所有属于该角色的用户会自动生效,不需要逐个用户修改;给某个人加权限,只需要给他分配一个合适的角色,不需要改动任何业务代码。权限管理从一个“改代码”的事情,变成了一个“配数据”的事情。

权限(Permission)这个词需要准确理解。在RBAC里,权限通常分解成两个维度:资源(Resource)和操作(Action)。资源就是系统里被保护的对象,比如“用户管理页面”“订单数据”“导出接口”;操作就是对资源能做什么,常见的有增、删、改、查、导出、审批。一个完整的权限点可以表述为“订单管理的导出权限”“用户列表的删除权限”。

把权限拆成“资源+操作”两个维度,后续做权限点管理、页面按钮控制、接口拦截都会顺很多。这也是很多成熟权限框架的标准做法。

2. 模型选型:从RBAC0到RBAC3怎么选

2.1 四个子模型的实际差别

很多同学第一次接触RBAC,被RBAC0、RBAC1、RBAC2、RBAC3这些名词绕晕了。其实它们不是四种并列的方案,而是四个递进的复杂度层次。

RBAC0是基础模型,包含用户、角色、权限三要素,以及用户-角色、角色-权限两个多对多关系。大多数中小型系统的需求,RBAC0就已经完全够用。我见过不少团队一上来就设计“角色继承+角色互斥+角色基数限制”的复杂模型,最后落地时发现维护成本极高,根本没业务场景用得上。

RBAC1在RBAC0的基础上增加了角色继承。角色之间可以形成上下级关系,比如“部门经理”角色继承“员工”角色的所有权限,再额外加一些审批权限。这样可以减少角色之间的权限重复配置。

RBAC2在RBAC0的基础上增加了约束条件。常见约束有:

  • 互斥角色:同一个用户不能同时拥有两个互斥的角色,比如“采购员”和“审批员”不能是同一个人,否则就会出现自己采购自己审批的问题。
  • 基数约束:某个角色最多能分配给多少个用户,比如“总经理”角色只能分配给1个人。
  • 先决角色:用户必须先拥有某个角色,才能拥有另一个角色,比如必须先当“员工”才能当“经理”。

RBAC3则是RBAC1和RBAC2的合集,既有角色继承,又有约束条件。这是理论上的最完整模型,也是实际项目里最不建议一上来就用的模型,因为约束规则越多,出错的可能性和排查难度就越大。

2.2 我的选型建议

结合我经手的项目经验,选型的核心原则是:权限模型只做当前业务需要的部分,不要为了“架构完善”而堆复杂度。过早引入角色继承和互斥约束,往往会在迭代过程中把自己绊倒。

如果是常规的企业后台、内容管理系统、SaaS用户端,优先选择RBAC0。把用户、角色、权限、用户角色关联、角色权限关联这五张表设计清楚,就已经能支撑绝大多数需求。

如果系统里有明显的组织层级和角色上下级关系,比如子管理员、区域负责人这类场景,可以考虑引入RBAC1的角色继承,让“子角色”自动继承“父角色”的权限。这里建议用“单独的角色等级字段”或者“角色树”来实现,而不是把继承逻辑写死在代码里。

如果有合规审计类需求,比如财务系统、审批系统里要求“申请”和“审批”不能是同一角色,才需要引入RBAC2的互斥角色约束。

我见过一个比较典型的反面案例:某团队在项目初期就设计了五层角色继承树加三种互斥约束,结果后续每次加新角色都要理清楚“继承链影响了谁”“和哪个角色互斥”,光角色配置就成了一门玄学。最终的解决办法反而是砍掉两层继承,回归扁平化设计。权限模型不是一个“越高级越好”的东西,它更像是一把锁,满足安全等级的前提下,越简单越好维护。

3. 数据库设计与表结构实现

3.1 五张核心表的落地设计

数据库是整个RBAC落地的地基。无论前端怎么做菜单动态展示,后端怎么做接口拦截,最终依赖的都是数据表里的关联关系。基于RBAC0,一套最经典的设计是五张表。

先看用户表。用户表不需要任何权限相关字段,它只负责用户自身的账号信息。

CREATE TABLE `sys_user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(200) NOT NULL COMMENT '密码(存储加密后密文)', `real_name` varchar(50) DEFAULT NULL COMMENT '姓名', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:1启用 0禁用', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

角色表负责定义系统中的角色,注意加一个角色编码字段。编码是代码里用的唯一标志,名称是给人看的,方便后续国际化或者名称调整。

CREATE TABLE `sys_role` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_code` varchar(50) NOT NULL COMMENT '角色编码', `role_name` varchar(50) NOT NULL COMMENT '角色名称', `description` varchar(200) DEFAULT NULL COMMENT '角色描述', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:1启用 0禁用', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_role_code` (`role_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

权限表存储的是细粒度的权限点。这里最关键的字段是“资源类型”和“操作类型”。资源类型可以区分菜单、按钮、接口、数据;操作类型对应增删改查导出等动作。早期系统可以只存功能权限,但建议预留这两个字段,避免后续加数据权限时又要改表。

CREATE TABLE `sys_permission` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '权限ID', `perm_code` varchar(100) NOT NULL COMMENT '权限编码,如 user:export', `perm_name` varchar(50) NOT NULL COMMENT '权限名称', `resource_type` varchar(20) NOT NULL COMMENT '资源类型:menu 菜单 / button 按钮 / api 接口 / data 数据', `action_type` varchar(20) NOT NULL COMMENT '操作类型:create/update/delete/query/export/audit', `path` varchar(200) DEFAULT NULL COMMENT '资源路径,菜单路由或接口URL', PRIMARY KEY (`id`), UNIQUE KEY `uk_perm_code` (`perm_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';

用户角色关联表把用户和角色关联起来,角色权限关联表把角色和权限关联起来。两张关联表结构类似,以用户角色关联表为例:

CREATE TABLE `sys_user_role` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '用户ID', `role_id` bigint NOT NULL COMMENT '角色ID', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_role` (`user_id`, `role_id`), KEY `idx_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';

有一个我踩过的坑需要特别提醒:用户角色关联表不要只给user_id建普通索引,要把(user_id, role_id)建成联合唯一索引。否则同一个人分配同一个角色两次,中间表里出现重复数据,后面统计权限时可能会出现权限重复,前端判断逻辑如果不做去重,还会出现菜单重复显示的问题。

3.2 关联关系的细节处理

五张表的关联查询是RBAC实现里最频繁的操作。查询一个用户拥有的所有权限,标准SQL是:

SELECT DISTINCT p.perm_code FROM sys_user u JOIN sys_user_role ur ON ur.user_id = u.id JOIN sys_role r ON r.id = ur.role_id JOIN sys_role_permission rp ON rp.role_id = r.id JOIN sys_permission p ON p.id = rp.permission_id WHERE u.id = #{userId} AND u.status = 1 AND r.status = 1;

这里用DISTINCT去重是有必要的。因为一个用户可能有多个角色,两个角色里如果配了同一个权限点,不加DISTINCT就会出现重复行。虽然代码层面可以做去重,但SQL层面去重一次,后面的逻辑会简单很多。

还需要考虑用户禁用的场景。用户被禁用之后,他的所有角色和权限应该同时失效,所以查询条件里要带上用户状态和角色状态的过滤。

关联表的级联删除也要仔细设计。删除一个角色时,sys_role_permission表里对应的关联数据应该一并删除,否则会留下大量孤儿数据。我建议在代码层面处理关联删除,不要过度依赖数据库的外键级联。外键在数据量上来之后会有性能和维护成本的隐患,而且一旦在多个服务之间拆分了权限库,外键就成了麻烦。统一的逻辑是:删除角色前先查有没有用户还在使用该角色,如果有就提示“该角色已分配给N个用户”,确认后再执行角色删除和关联数据清理。删除用户同理。

索引方面,两张关联表的唯一索引已经覆盖了最常见的查询方向(通过user_id查角色、通过role_id查权限)。如果业务里经常出现“查询拥有某个角色的所有用户”这种反向查询,再给另一个字段单独加索引。不要在一开始就创建一堆索引,等实际慢查询出现后再针对性地加。

4. 权限校验的核心代码实现

4.1 登录时如何加载权限数据

数据库设计好了,接下来是代码实现。权限校验的第一个环节是登录成功后,把当前用户的角色和权限点加载进内存,后续的每一次请求都在内存里做判断,而不是每次都去数据库查一遍权限。

我常用的做法是登录成功后生成一个“登录用户上下文”对象,里面包含用户基本信息、角色编码集合、权限编码集合。无论用什么框架承载,这个对象的本质是“当前这次会话中,用户被允许做什么”的数据快照。

以Java为例,登录成功后的核心逻辑大致是这样的:

public LoginUser buildLoginUser(Long userId) { List<String> roleCodes = userRoleService.listRoleCodesByUserId(userId); List<String> permCodes = permissionService.listPermCodesByUserId(userId); LoginUser loginUser = new LoginUser(); loginUser.setUserId(userId); loginUser.setRoleCodes(roleCodes); loginUser.setPermCodes(permCodes); return loginUser; }

loginUser会被写入会话上下文,比如Spring Security的Authentication对象里,或者是一个ThreadLocal变量里。后面的接口拦截、按钮权限判断,全都从这个上下文里取数据。

从性能角度考虑,我建议权限的加载过程做成一次查询。也就是用一个专门的mapper方法,直接把用户ID传给一条联表SQL,一次查出所有权限编码。不要写一个循环,在每个角色里分别查一次权限,那样会白白增加数据库压力。

权限数据的存储位置有两种常见选择:一种是直接存内存(登录态内存对象),适合单体应用;另一种是登录成功后写入Redis,适合多实例部署的分布式系统。多实例部署时,用户在A机器登录,下一次请求可能被负载均衡转发到B机器,如果权限数据只存在单机内存里,B机器就识别不了用户权限。所以只要系统不是单节点部署,就应该用Redis存权限快照,key用userId或者sessionId,value是序列化后的角色、权限集合。

4.2 后端接口拦截与按钮权限

后端接口权限校验是整个权限体系里最不能省的一环。前端隐藏按钮、隐藏路由都只是体验层面的优化,真正安全的系统必须保证后端接口本身有权限校验,否则任何人都能绕过界面直接构造请求。

实现方式最常用的是“注解+拦截器/AOP”的模式。定义一个权限注解,比如:

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermission { String value(); }

然后在Controller接口上声明需要的权限编码:

@RequiresPermission("order:export") @PostMapping("/export") public void export(@RequestBody ExportReq req) { orderService.export(req); }

拦截器里统一校验:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; RequiresPermission annotation = method.getMethodAnnotation(RequiresPermission.class); if (annotation == null) { return true; } LoginUser loginUser = getCurrentUser(); if (loginUser == null || !loginUser.getPermCodes().contains(annotation.value())) { throw new ForbiddenException("无权访问"); } } return true; }

这里有一个容易被忽视的细节:注解如果既能在类上声明、又能在方法上声明,拦截器里要自己定义优先级规则。我的规则是方法上的注解优先于类上的注解。如果一个Controller类上标注了“order:view”,其中某个方法标注了“order:export”,那这个方法就应该只校验export权限,不要两个都校验、更不要只校验类上的view权限。

还有一个面向接口设计的原则:资源的粒度要统一。同一类资源的访问权限,尽量用同一个权限编码去控制,不要在A接口里用“order:export”,在B接口里又用“export:order”,前后编码不一致,配置权限点的时候非常容易混乱。权限编码我习惯用“资源:操作”的格式,比如user:create、order:delete、report:export。这种格式本身就是一个自解释的字符串,排查问题时一眼就能看出这是“用户创建”“订单删除”“报表导出”。

对于白名单接口,比如登录接口、图片验证码、健康检查,要配置在拦截器的排除列表里。但有一个原则:白名单宁可少配,不可多配。我见过一个团队为了方便调式,把整个/test/**路径放进了白名单,结果生产环境也继承了配置,等于给所有接口开了口子。白名单路径最好用精确路径,不要用模糊匹配,更不要用“/controller/**”这种宽泛前缀。

4.3 前端动态菜单与按钮级权限

后端校验做完了,前端还有一层配合工作。前端没有权限校验的职责,但它承担着“没有权限就不展示入口”的体验职责。

动态菜单的实现思路是:登录成功后,后端返回当前用户可访问的菜单树,前端根据这份菜单树动态注册路由与渲染侧边栏。菜单树的构建过程实际上是把sys_permission表中resource_type为menu的数据,按照父子关系组装成一棵树。每个菜单项关联一个权限编码或者路由地址,前端拿到后过滤掉没有权限的菜单项。

按钮级权限可以用一个小型指令或者自定义钩子来实现。比如在Vue工程里,我习惯封装一个v-permission指令:

const permission = { mounted(el, binding) { const requiredPerm = binding.value; const userPerms = store.getters.permCodes; if (!userPerms.includes(requiredPerm)) { el.parentNode && el.parentNode.removeChild(el); } } }; app.directive('permission', permission);

模板里的用法是:

<el-button v-permission="'user:export'" @click="handleExport">导出</el-button>

这样写的好处是整个项目里不会出现任何“if have permission then show”的重复代码,权限控制的声明就写在按钮旁边,可读性很高。

但我在实际项目中使用按钮指令时也有过教训。前端根据权限隐藏按钮,只能解决“看不见”的问题。如果有用户知道这个接口的地址,绕过界面直接调用,后端拦截不严一样能成功。所以我在任何一次权限评审里都会强调:前端按钮隐藏是用户体验,后端接口校验才是安全底线。两者不是“二选一”,而是“都要做”。

5. 实际项目中的常见问题与排查技巧

5.1 权限变更不生效的问题

RBAC系统上线之后,最常被业务同事吐槽的就是“我给这个用户换了角色,他那边怎么还是老权限”。

如果权限数据是登录时一次性加载到内存的,那用户当前正在进行的会话里不重新登录,就不会刷新权限。解决办法通常有两种思路:一种是登录态过期策略,用户重新登录后加载最新权限;另一种是权限变更时主动刷新。

对于后者,我建议在角色权限变更、用户角色变更的业务逻辑里,主动清理该用户(或所有受影响用户)的权限缓存。具体是:修改角色的权限关联之后,查出这个角色分配给了哪些用户,然后删除这些用户在Redis里的权限key;给用户调整角色之后,直接删除该用户的权限key。这样用户下一次请求时发现缓存中没有权限数据,就会从数据库重新加载,达到“改完即生效”的效果。

我踩过的坑是:只清理了用户角色缓存,忘了清理角色权限缓存。结果角色权限改了,用户身上的角色没变,但因为缓存的权限点没有失效,用户看到的还是旧权限。正确做法是“角色权限变更”和“用户角色变更”两个入口都要做缓存清理。

还有一个特殊情况:超级管理员。很多系统里super_admin的判断是硬编码在代码里的,比如判断用户ID等于1,或者角色编码属于固定集合。这种情况我建议在权限查询层就统一处理,不要散落得到处都是。统一处理的逻辑是:如果用户是超级管理员角色,直接返回所有权限点,不做数据库关联查询。这样既保证了超管的权限完整性,又避免超管被误改角色后失去系统入口的尴尬。

5.2 N+1查询与权限性能优化

权限数据查询里最容易出现的性能问题就是N+1查询。所谓N+1,就是先查了一次用户列表,得到N个用户,然后循环每个用户去查询他的角色和权限,这就会执行1+N次SQL。用户量上了1000,这个查询模式就会非常明显。

解决思路有两个层次。第一个层次是“批量加载”:先查出所有用户的ID集合,再用WHERE user_id IN (...)一次性查出所有关联关系,在内存里做组装。这一招能把几十次查询压缩成两三次。

第二个层次是“缓存权限快照”。对于读多写少的权限数据,Redis缓存的价值非常大。用户登录后把权限编码集合以Set结构存入Redis,key的设计建议是perm:user:{userId}。每次请求做权限校验时,只需执行一次SISMEMBER perm:user:{userId} order:export,时间复杂度是O(1),性能远高于每次查数据库。

权限点和角色数据量都不大,大部分项目也就是几百条权限、几十个角色。这种量级其实完全可以把权限点全表缓存在本地内存里,权限变更时再刷新。但是要注意缓存和数据库的一致性,最好的做法是“缓存只承载读,写入永远走数据库,写完主动清缓存”。不要试图去给缓存设置一个很长的过期时间来做兜底,权限数据一旦过期期间不同步,就会引发权限校验结果不一致的问题。

还有一个实践细节:权限校验的异常返回要区分“未登录”和“无权限”两种情况。未登录返回401,前端跳转登录页;无权限返回403,前端提示“当前账号无操作权限”。很多系统把这两种状态混在一起,用户明明登录了却总是被误踢到登录页,体验很差。排查这类问题时,先看返回状态码,再定位是拦截器顺序问题还是权限校验逻辑问题。

5.3 从功能权限走向数据权限

前面讨论的都是功能权限,也就是“能不能点这个按钮”“能不能访问这个接口”。RBAC模型还经常被问到的一个扩展方向是数据权限,也就是“能看哪些数据”。

典型的场景是:销售主管能看自己部门所有销售的数据,销售专员只能看自己的数据。这种需求靠功能权限解决不了,因为功能权限只告诉系统“这个人可以查看客户列表”,没告诉系统“这个人能查看哪些客户”。

我的建议是:数据权限不要一开始就混进RBAC的权限表里硬做,而是要单独建立数据范围规则。常见做法是给角色增加一个数据范围字段,例如:全部数据、本部门及以下、本部门、本人、自定义。然后在业务查询接口里,根据当前用户的数据范围,自动拼接数据过滤条件。

举一个比较常见的实现方式:在用户表关联一个部门字段,在角色表增加data_scope字段,查询客户列表时根据当前用户主角色判断数据范围,拼入WHERE dept_id = ?或者WHERE create_by = ?条件。这个逻辑可以封装成一个“数据权限切面”,统一处理,避免每个业务接口自己写一遍。

这里要特别提醒一个容易出错的地方:数据权限的过滤条件,必须在下层SQL里生效,不能在应用层查到全量数据后再做内存过滤。内存过滤表面上节省了开发量,但数据量一大,内存和响应时间都会失控,而且存在“先把不该看的数据加载到内存”的安全隐患。正确做法是让数据库在查询阶段就屏蔽掉无权限的数据行,业务层拿到的已经是过滤后的结果。

我在一个客户管理项目里就遇到过类似问题。当时为了快速上线,某个统计接口用了“查全量数据+内存过滤”的方式,数据量到达几十万行之后,接口相应变得极慢,最终还是要回归到SQL层面去拼权限条件。所以数据权限设计一定要趁早,越晚重构代价越大。

再补充一点:数据权限和角色继承配合时容易有语义歧义。比如上级角色数据范围是“本部门及以下”,下级角色数据范围是“本人”,当一个用户同时拥有这两个角色时,数据范围到底取哪个?我建议在处理时明确优先级规则:直接指定的数据范围优先,或者取最严格的数据范围。规则怎么定都可以,但要明确写进设计文档,且要在权限判断的统一地方实现,不要让每个接口自己判断,否则很容易出现“同一个用户在不同页面看到的范围不一样”的诡异现象。

这里也顺带讲讲RBAC的边界。RBAC本身只解决用户和权限的映射关系,不要指望它处理所有安全问题。密码安全、越权漏洞、操作审计、敏感数据加密这些都需要独立的机制来保障。我在做权限体系时,会同时叠加一套操作日志和审计机制,在关键接口上记录“什么人、什么时间、对什么资源、做了什么操作”。没有审计的权限体系是不完整的,因为出了问题没有任何追溯能力。

最后分享一个真实体会

权限模块是我经手过的最容易“一开始觉得简单,越做越复杂”的模块之一。RBAC模型看起来就是几张表、几个关联关系,真正落到项目里才会发现,细节藏在权限点的粒度划分、缓存一致性、数据范围规则这些地方。我的体会是:先守住RBAC0的基础设计,把用户、角色、权限、关联关系都做得干净利落,不要用角色字符串判断、不要把权限判断散落到各个业务代码里、不要只做前端拦截不做后端校验,这几点做到了,权限体系就已经比大多数系统靠谱了。

如果后续有需要,可以在RBAC0的基础上逐步叠加角色继承、互斥约束、数据范围,不要一次性把模型设计得过于复杂。权限系统不是越复杂越安全,而是越清晰越安全。希望这篇基于实际经验的拆解,能帮你少走一些我走过的弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 8:34:53

微博话题风险检测:构建内容安全的实时风控流水线

1. 项目概述&#xff1a;为什么微博话题检测不是“抓热词”而是守内容安全的哨岗“信息内容安全-微博话题检测及分析”这个标题里&#xff0c;“信息内容安全”是目标&#xff0c;“微博话题”是对象&#xff0c;“检测及分析”是动作——但千万别把它当成一个简单的“爬热搜榜…

作者头像 李华
网站建设 2026/10/23 19:49:29

RDKit从SMILES到3D构象:2D与3D分子处理全解析

作为一个每天跟分子结构打交道的人&#xff0c;我太清楚“手里攒了几千条SMILES&#xff0c;接下来怎么办”这种尴尬了。很多人装好RDKit之后&#xff0c;熟悉的就是 Chem.MolFromSmiles 画个分子&#xff0c;或者算个分子量&#xff0c;然后就没有然后了。这里想说的&#x…

作者头像 李华
网站建设 2026/10/23 8:40:25

F28035 HRCAP高分辨率捕获模块自学指南:MEP标定与寄存器配置实战

做电机控制或者数字电源的朋友&#xff0c;对 F28035 这颗芯片应该不陌生。它的 HRCAP 模块&#xff0c;全称 High-Resolution Capture&#xff0c;也就是高分辨率捕获模块&#xff0c;官方文档里写得很全&#xff0c;但说实话&#xff0c;自学门槛并不低。如果你只是像看小说一…

作者头像 李华
网站建设 2026/10/23 8:47:47

RabbitMQ Consumer动态上下线实战:基于心跳的服务自治

1. 项目概述&#xff1a;为什么“动态上下线Consumer”不是锦上添花&#xff0c;而是生产环境的生存刚需在某次为某高校实验室搭建实时日志分析系统时&#xff0c;我遇到一个典型场景&#xff1a;凌晨三点&#xff0c;系统突然收到一批异常高频的设备心跳包&#xff0c;监控告警…

作者头像 李华
网站建设 2026/10/23 8:48:29

Python 3.10模式匹配实战:从基础语法到高级应用

1. Python模式匹配入门指南第一次在Python 3.10的更新日志里看到match-case语法时&#xff0c;我的反应和大多数Python老手一样&#xff1a;"这不就是其他语言里的switch-case吗&#xff1f;"但当我真正开始使用后才发现&#xff0c;Python的模式匹配远不止简单的条件…

作者头像 李华