企业认证与安全体系(八):企业为什么都在用 RBAC?一篇讲透权限模型设计
上一篇我们讲了《企业认证与安全体系七OAuth2 到底解决了什么问题一篇讲透授权与第三方登录》到这里我们已经讲清楚了很多“认证”相关的问题双 Token Token Redis JWT Spring Security Gateway OAuth2 第三方登录这些内容主要解决的是你是谁 你怎么登录 你的登录态怎么维护 你的请求怎么被识别但是在企业系统里仅仅知道“你是谁”还不够。系统还必须知道你能访问哪些菜单 你能点击哪些按钮 你能调用哪些接口 你能看到哪些数据这就进入了另一个非常重要的问题授权。认证解决的是你是谁授权解决的是你能干什么而企业系统中最常见的授权模型就是RBAC。今天我们就从工程视角讲透企业为什么都在用 RBAC以及权限模型到底该怎么设计。一、先区分认证和授权很多人刚接触安全体系时会把认证和授权混在一起。其实它们完全不是一回事。认证是 Authentication。授权是 Authorization。简单来说概念解决的问题例子认证你是谁用户登录成功系统知道你是张三授权你能干什么张三能不能删除订单例如用户登录成功后系统知道userId 1001 username zhangsan这只是认证完成了。但是用户能不能进入后台能不能看订单能不能删除订单能不能导出财务报表这些不是认证问题而是授权问题。所以企业系统必须在认证之后继续做权限控制。二、如果没有权限模型会怎样假设一个后台系统有这些功能用户管理 订单管理 商品管理 财务报表 系统配置不同岗位的人能操作的内容肯定不一样。例如用户应该拥有的权限超级管理员所有权限运营人员商品、订单相关权限财务人员财务报表权限客服人员查询订单、处理售后普通员工只能查看部分信息如果没有权限模型最容易写成这样if (user.getUsername().equals(admin)) { // 可以删除订单 }或者if (user.getType() 1) { // 可以查看财务报表 }一开始看起来没问题。但是项目一复杂问题马上出现。三、为什么不能到处写 if(admin)企业项目最怕的就是权限逻辑散落在各个地方。例如if (isAdmin) { deleteOrder(); }if (userType 2) { exportReport(); }if (roleName.equals(manager)) { updateUser(); }这种写法有几个明显问题。1. 权限逻辑分散权限判断散落在 Controller、Service、前端页面中。后期想改权限规则时根本不知道哪里写了判断。2. 角色和代码强绑定代码里写死admin manager userType 1以后角色变了代码也要跟着改。3. 不适合后台动态配置企业后台经常需要在页面上配置给张三分配运营角色 给李四分配财务角色 给王五取消删除权限如果权限写死在代码里后台根本没法动态管理。4. 容易产生安全漏洞某个接口忘了判断权限用户就可能越权访问。例如普通用户直接请求DELETE /order/1001如果后端没做权限校验就会出现严重安全问题。所以企业系统不会到处写if(admin)。而是会设计一套统一的权限模型。这就是 RBAC。四、RBAC 到底是什么RBAC 全称是Role-Based Access Control中文叫基于角色的访问控制它的核心思想是用户不直接绑定权限而是通过角色获得权限。也就是说用户 ↓ 角色 ↓ 权限例如张三 ↓ 运营角色 ↓ 商品管理、订单查询、活动配置李四 ↓ 财务角色 ↓ 财务报表、订单金额、发票管理这样做的好处是权限不直接绑在人身上而是绑在角色上。用户只要分配角色就自动拥有角色对应的权限。五、RBAC 的三个核心对象RBAC 里最核心的三个对象是用户 角色 权限1. 用户User用户就是系统中的具体账号。例如张三 李四 王五 admin用户是登录主体。认证阶段解决的是用户是谁。2. 角色Role角色是一组权限的集合。例如超级管理员 运营人员 财务人员 客服人员 普通员工角色不是具体的人而是一类职责。3. 权限Permission权限表示某个具体操作能力。例如user:add user:delete order:list order:detail order:delete report:export system:config权限越细控制越精确。六、用户、角色、权限之间的关系RBAC 的关系一般是一个用户可以有多个角色 一个角色可以分配给多个用户 一个角色可以拥有多个权限 一个权限可以分配给多个角色所以关系如下User ↓ UserRole ↓ Role ↓ RolePermission ↓ Permission这是典型的多对多关系。例如张三 ↓ 运营角色 客服角色 ↓ 订单查询 商品编辑 售后处理张三可以同时拥有多个角色。系统最终判断的是张三拥有的所有角色 ↓ 这些角色对应的所有权限七、RBAC 常见数据库表设计一个基础 RBAC 权限模型通常会有这些表。sys_user sys_role sys_permission sys_user_role sys_role_permission1. 用户表sys_userCREATE TABLE sys_user ( id BIGINT PRIMARY KEY, username VARCHAR(64), password VARCHAR(255), status TINYINT, create_time DATETIME );用户表保存账号基础信息。2. 角色表sys_roleCREATE TABLE sys_role ( id BIGINT PRIMARY KEY, role_code VARCHAR(64), role_name VARCHAR(64), status TINYINT, create_time DATETIME );例如role_code ADMIN role_name 超级管理员3. 权限表sys_permissionCREATE TABLE sys_permission ( id BIGINT PRIMARY KEY, permission_code VARCHAR(128), permission_name VARCHAR(64), permission_type TINYINT, parent_id BIGINT, path VARCHAR(255), create_time DATETIME );这里有几个关键字段字段含义permission_code权限标识permission_name权限名称permission_type权限类型parent_id父级权限path菜单路径或接口路径4. 用户角色表sys_user_roleCREATE TABLE sys_user_role ( id BIGINT PRIMARY KEY, user_id BIGINT, role_id BIGINT );表示用户拥有哪些角色。5. 角色权限表sys_role_permissionCREATE TABLE sys_role_permission ( id BIGINT PRIMARY KEY, role_id BIGINT, permission_id BIGINT );表示角色拥有哪些权限。八、菜单权限、按钮权限、接口权限企业后台权限通常不只是“能不能登录”。它一般分成三类菜单权限 按钮权限 接口权限有些系统还会加上数据权限1. 菜单权限菜单权限控制的是用户能不能看到某个菜单例如用户管理 订单管理 财务报表 系统设置运营人员可能看不到“财务报表”。普通员工可能看不到“系统设置”。这就是菜单权限。2. 按钮权限按钮权限控制的是用户能不能执行某个页面操作例如订单页面里有这些按钮新增订单 删除订单 导出订单 审核订单某个用户可以查看订单列表但不能删除订单。这就是按钮权限。3. 接口权限接口权限控制的是用户能不能调用某个后端接口例如GET /order/list POST /order/add DELETE /order/{id} POST /order/export前端隐藏按钮只是体验优化。真正安全必须靠后端接口权限。因为用户可以绕过前端直接调用接口。所以企业项目中必须记住一句话前端控制显示后端控制安全。4. 数据权限数据权限控制的是用户能看到哪些数据例如销售只能看自己的客户 部门经理能看本部门客户 区域经理能看整个区域客户 总部管理员能看全部客户菜单、按钮、接口控制的是“能不能操作”。数据权限控制的是“能操作哪些数据”。这是企业系统里最容易复杂的地方。九、权限标识怎么设计权限标识一般会设计成字符串。例如user:list user:add user:update user:delete order:list order:detail order:export system:config这种格式好处是清晰。一般遵循模块:操作例如user:add表示用户模块的新增权限order:export表示订单模块的导出权限这样前后端都容易理解。十、登录后权限怎么加载用户登录成功后系统一般会加载用户权限。流程如下用户登录 ↓ 查询用户信息 ↓ 查询用户角色 ↓ 查询角色权限 ↓ 生成权限集合 ↓ 放入 LoginUser ↓ 生成 Token例如LoginUser loginUser new LoginUser(); loginUser.setUserId(user.getId()); loginUser.setUsername(user.getUsername()); loginUser.setPermissions(permissionSet);权限集合可能是[ user:list, user:add, order:list, order:export ]后续访问接口时系统就可以判断当前用户是否拥有某个权限十一、Spring Security 中如何使用权限在 Spring Security 中权限最终会放到Authentication里面。例如ListGrantedAuthority authorities permissions.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList());然后创建认证对象UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken( loginUser, null, authorities );最后放入SecurityContextHolder.getContext().setAuthentication(authentication);这样 Spring Security 就知道当前用户有哪些权限。十二、接口上如何做权限控制常见做法是使用注解。例如PreAuthorize(hasAuthority(order:export)) PostMapping(/order/export) public void exportOrder() { // 导出订单 }含义是只有拥有 order:export 权限的用户 才能调用这个接口再比如PreAuthorize(hasAuthority(user:delete)) DeleteMapping(/user/{id}) public void deleteUser(PathVariable Long id) { // 删除用户 }这样权限控制就集中在接口入口处。而不是在业务代码里到处写if (isAdmin) { }这就是企业项目更推荐的方式。十三、前端如何使用权限后端返回用户权限集合{ permissions: [ user:list, user:add, order:list, order:export ] }前端根据权限控制菜单和按钮显示。例如有 user:list 显示用户管理菜单 有 order:export 显示导出按钮但是要注意前端权限控制只是为了用户体验不是安全边界。真正的安全边界一定在后端。因为前端代码可以被调试、篡改、绕过。十四、RBAC 和 JWT、Redis 的关系前面我们讲了 JWT 和 Redis。这里再把关系串起来。JWT 负责证明你是谁Redis 负责控制你的登录态是否有效RBAC 负责判断你能干什么它们不是互相替代关系。而是分工不同。JWT身份认证 Redis会话控制 RBAC权限授权完整流程是用户登录 ↓ 认证成功 ↓ 加载角色和权限 ↓ 生成 Token ↓ 请求接口 ↓ 校验 Token ↓ 检查 Redis 会话 ↓ 检查接口权限 ↓ 执行业务十五、RBAC 能解决所有权限问题吗RBAC 很常用但它不是万能的。它适合解决谁能访问什么功能 谁能点击什么按钮 谁能调用什么接口但是面对复杂数据权限时RBAC 往往不够。例如只能看本部门数据 只能看自己创建的数据 只能看所在城市的数据 只能看负责客户的数据这时候通常会在 RBAC 基础上继续扩展数据权限。例如角色 部门 角色 数据范围 角色 租户 角色 组织树所以企业系统常见做法是RBAC 解决功能权限数据权限单独扩展。十六、常见权限模型演进一个企业后台权限系统通常会经历几个阶段。第一阶段写死管理员判断if (isAdmin) { // 允许操作 }适合 Demo不适合企业项目。第二阶段角色判断if (hasRole(ADMIN)) { // 允许操作 }比写死用户好但仍然不够灵活。第三阶段权限点判断PreAuthorize(hasAuthority(order:export))这是企业系统更常见的方式。第四阶段RBAC 数据权限角色控制功能 数据权限控制数据范围这是成熟企业后台常见方案。十七、面试怎么回答 RBAC如果面试官问RBAC 是什么可以这样答RBAC 是基于角色的访问控制模型。它的核心思想是用户不直接绑定权限 而是通过角色获得权限基本关系是用户 - 角色 - 权限一个用户可以有多个角色一个角色可以拥有多个权限。企业系统通常会用 RBAC 来实现菜单权限、按钮权限和接口权限。在后端实现上一般会设计用户表、角色表、权限表、用户角色关联表、角色权限关联表。登录时加载用户权限接口访问时通过 Spring Security 或自定义注解进行权限校验。需要注意的是RBAC 主要解决功能权限复杂的数据权限通常需要在 RBAC 基础上继续扩展。十八、最终核心理解到这里我们可以把认证和授权彻底区分开。认证解决的是你是谁授权解决的是你能干什么JWT、Redis、Spring Security、Gateway 主要帮助我们完成认证链路和统一鉴权入口。而 RBAC 解决的是企业系统里的权限分配问题。它通过用户 ↓ 角色 ↓ 权限把复杂权限管理抽象成一个可配置、可维护、可扩展的模型。所以企业系统普遍使用 RBAC不是因为它高级而是因为它足够稳定、清晰、可维护。对于大多数后台系统来说RBAC 是权限体系的基础。下篇预告下一篇我们继续《企业认证与安全体系九单点登录 SSO 到底是怎么实现的一篇讲透企业统一身份认证》前面我们已经讲了JWT Redis Gateway OAuth2 RBAC下一篇开始进入企业多系统登录问题。我们将讲透什么是 SSO 为什么企业需要单点登录 多个系统如何共享登录态 SSO 和 OAuth2、OIDC 是什么关系 统一身份认证平台如何设计 为什么登录一次可以访问多个系统真正把企业统一身份认证体系串起来。