上一篇我们讲了:
《企业认证与安全体系(七):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_user
CREATE TABLE sys_user ( id BIGINT PRIMARY KEY, username VARCHAR(64), password VARCHAR(255), status TINYINT, create_time DATETIME );用户表保存账号基础信息。
2. 角色表:sys_role
CREATE 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_permission
CREATE 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_role
CREATE TABLE sys_user_role ( id BIGINT PRIMARY KEY, user_id BIGINT, role_id BIGINT );表示用户拥有哪些角色。
5. 角色权限表:sys_role_permission
CREATE 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里面。
例如:
List<GrantedAuthority> 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 是什么关系 统一身份认证平台如何设计 为什么登录一次可以访问多个系统真正把企业统一身份认证体系串起来。