律所案件管理系统源码开发的权限体系实例设计:RBAC与数据范围授权的实现

导语

一套同时管住功能按钮与案卷数据的权限体系,是律所案件管理系统能否在合伙制律所稳定运行的前提:律师对案卷有天然私密要求,合伙人与风控需要跨案件的全局视图,行政与助理又承担大量公共事务,几类诉求彼此冲突。业界通行的解法是正交的两层模型------纵向功能权限决定能否操作菜单与按钮,横向数据范围决定能看到哪些案件与字段。本篇以"用户-角色-权限"为骨架,沿数据范围授权、案件级隔离、按钮与字段级落库、审计留痕的顺序展开,并给出权限表结构与 SQL 过滤示例。

一、RBAC 底座:用户-角色-权限与最小权限

RBAC(Role-Based Access Control)在律所业务中表现为三段模型:用户只挂角色,角色承载一组权限码,权限码对应资源与动作的组合。用户不直接持有权限,所有授权变更通过调整"用户-角色"关系完成。这么做的收益在律所尤其明显------助理离职、律师转所、新合伙人入伙是常态,若把权限直接绑在用户上,每次人员交接都是一场权限盘点;绑在角色上,只需解绑或重绑一条关系,再把历史承办案件的数据范围交给流程自动回收。

国内律所产品的角色集高度趋同,以下五类常设角色可作为岗位模板。模板应当收敛,具体律所可按二级专业化拆分(如把"律师"拆为"诉讼主办律师""非诉律师"),但应避免演变成一人一角色的扁平化授权。

角色 功能权限特征 默认数据范围 备注
管理员 组织架构、角色授权、字典、流程引擎配置 元数据全量,不默认触碰案卷 通常不参与具体办案
合伙人 收结案审批、经营与风控报表 本团队/全所(按所规配置) 需掌握全局视图
律师 承办案件全流程操作、文书签发 本人主办 + 协办 核心业务角色
助理 文书起草、材料归档、日程维护 所协助案件(多为只读或字段受限) 写入需复核
行政 利冲检索、开票、用印、结案归档 全所(事务属性) 操作强留痕

模板之上是两条硬约束。其一是最小权限原则:每个角色只授予完成岗位职责所必需的权限码,宁可先收紧后放宽,也不要图省事勾"全选"。一个典型反面案例是给行政开通全部案件读写,导致结案归档时能改动承办律师的答辩状------这在授权模型上是缺陷,不是操作习惯问题。其二是判定逻辑收敛:一套成熟的律所案件管理系统的权限体系,会把功能与数据拆成正交的两个维度分别建模,统一走权限码判定,避免在代码里散落 if (user.role == "partner") 这类硬编码分支。

二、数据范围授权:把行级过滤固化到查询层

功能权限回答"能不能点",数据范围回答"点进去后能看哪些行"。只做前者不做后者的系统,会退化成"人人进得去案件列表、全靠自觉不点他人案卷",这在合规审计时无法交代。数据范围授权通常提供四档,律所按制度选择"逐角色取最小"还是"按组织层级放行":

数据范围档位 覆盖对象 行级过滤含义
本人主办 仅自己主办的案卷 案件归属人等于当前用户
主办 + 协办 承办团队内成员 用户出现在该案件承办团队中
本部门/团队 同一业务部门或团队 案件归属部门等于用户所属部门
全所 全部在办案件 无行级条件(通常限合伙人、风控)

落到 SQL 上,关键原则是过滤条件必须进 WHERE 子句,让数据库在扫描阶段剪掉无权行,而不是把全表捞进内存后由应用层逐行筛选------后者在分页、报表、导出三种场景下必然产生越权窗口。以"主办+协办"档为例,行级过滤通过承办关系表的 EXISTS 子查询实现:

sql 复制代码
SELECT c.case_no, c.case_name, c.matter_type, c.opening_date
FROM t_case c
WHERE c.is_deleted = 0
  AND EXISTS (
        SELECT 1
        FROM t_case_team ct
        WHERE ct.case_id   = c.id
          AND ct.user_id   = :current_user_id
          AND ct.is_active = 1
  )
ORDER BY c.opening_date DESC;

若用户命中"本部门"档,则改为在案件表上追加 AND c.org_id = :current_user_org_id;若命中"全所"档,则跳过 EXISTS 只保留公共过滤。所谓"查询按当前用户过滤"不是一句口号,而是一段随数据范围档位动态拼接的公共 SQL。判定建议收敛到统一入口,伪代码如下:

text 复制代码
function buildCaseRowScope(userId):
    scope = resolveDataScope(userId)          // 解析该用户生效的数据范围
    if scope == SCOPE_ALL:
        return ""                              // 全所可见,无行级条件
    if scope == SCOPE_DEPARTMENT:
        return " AND c.org_id = " + getOrgId(userId)
    if scope == SCOPE_TEAM:                    // 本人主办 + 协办
        return (" AND EXISTS (SELECT 1 FROM t_case_team ct "
              + "WHERE ct.case_id = c.id AND ct.user_id = "
              + userId + " AND ct.is_active = 1)")

列表、详情、报表、导出四类查询复用同一片段,是杜绝越权的第一道闸:过滤不发生在某个前端组件里,而是发生在每条查询必经的 SQL 层。

三、案件级隔离:承办 ACL、外协最小授权与密级字段

数据范围授权管住了"案件集合",但同一案件内部仍分层级:主办律师对一份答辩状可改可签发,协办律师通常只能编辑,助理可能只允许预览文书列表,而案卷里的报价与和解方案连协办都不一定该看。案件内部的读写边界由承办 ACL(访问控制表)承担,表结构可简化为:

sql 复制代码
CREATE TABLE t_case_team (
  case_id      BIGINT NOT NULL,
  user_id      BIGINT NOT NULL,
  member_type  TINYINT NOT NULL,   -- 1主办 2协办 3协作 4外聘
  can_sign     TINYINT DEFAULT 0,  -- 是否可签发文书
  can_export   TINYINT DEFAULT 0,  -- 是否可导出卷内材料
  grantor_id   BIGINT,             -- 授权人
  expire_at    DATETIME,           -- 外聘/临时授权到期时间
  PRIMARY KEY (case_id, user_id)
);

承办 ACL 的维护者是主办律师,新增协办人须在案件内完成授权并记录授权人;跨部门联合办案时,各主办律师在各自案件上互加对方为"协作成员",而不是给对方放开部门级权限。ACL 以案件为粒度,天然贴近律师对案卷的独占心理预期,也避免"范围放大后收不回来"的权限膨胀。

律所案件管理系统还必须处理两类"圈外人"。其一是客户:经客户门户(Client Portal)登录的账号只能关联其委托的案件,通常只读进展、文书与账单,连案件列表都不开放,只能从门户直达授权案件详情------这是最小授权的典型场景。其二是外部专家:外聘律师、鉴定专家以"外聘"成员类型进入承办团队,配临时身份并写入 expire_at,权限收敛为只读且禁止导出原件;此类账号单独标记,纳入定期失效巡检,结案即自动回收。

密级字段是案件级隔离的最后一层。标的额、和解方案、涉刑线索等属于高密级字段,系统按字段级权限返回脱敏值------助理看到的金额为掩码,持有对应字段权限码的角色才可见明文;查看高密级字段的动作同时叠加审计与水印,避免截图外传后无法追溯。

四、按钮级与字段级权限的落库、以及审计日志

按钮级权限的落库方式决定前端体验与后端安全是否一致。业内通用做法是用权限码(perm_code)统一表达菜单、按钮、字段三类资源,角色与权限通过关系表关联:

sql 复制代码
CREATE TABLE t_perm (
  perm_code  VARCHAR(64) PRIMARY KEY,  -- 如 case:close、case:delete
  perm_name  VARCHAR(64) NOT NULL,
  perm_type  TINYINT NOT NULL,         -- 1菜单 2按钮 3字段
  module     VARCHAR(32)
);
CREATE TABLE t_role_perm (
  role_id    BIGINT NOT NULL,
  perm_code  VARCHAR(64) NOT NULL,
  PRIMARY KEY (role_id, perm_code)
);

前端渲染按钮时,按当前用户的权限码集合决定隐藏或置灰;后端接口再校验一次,防止绕过 UI 直接调用接口。判定逻辑收敛为单一函数:

text 复制代码
function hasPerm(userId, permCode):
    roleIds = getUserRoleIds(userId)
    return rolePermDao.exists(roleIds, permCode)

字段级权限一般不另建权限表,而是在案件字段元数据上挂"所需权限码",查询时按用户权限对字段做裁剪或脱敏。数据范围档位已在角色上配置,无需重复落表。

审计日志是权限闭环的最后一环,用于回答"谁能看、谁能改、谁把谁加进了哪个案件、谁看过密级"。日志表只追加、不更新:

sql 复制代码
CREATE TABLE t_audit_log (
  id         BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id    BIGINT NOT NULL,
  case_id    BIGINT,                -- 可空:非案件类操作
  action     VARCHAR(32) NOT NULL,  -- LOGIN/QUERY/EXPORT/GRANT/VIEW_FIELD
  perm_code  VARCHAR(64),
  detail     JSON,                  -- 变更前后摘要
  ip         VARCHAR(64),
  created_at DATETIME
);

至少对以下动作强制留痕:登录、案件导出、卷宗下载、角色与权限变更、承办 ACL 增删、密级字段查看。日志库与业务库分离,仅允许插入,按月归档。出现越权争议时,靠 GRANT 类日志还原"谁在何时把谁加进了哪个案件、由谁授权、何时到期",这是权限体系可信度的最后保障。

实操要点

  • 角色收敛为岗位模板并遵循最小权限,用户只挂角色不直接绑权限码,人员变动只改"用户-角色"关系,承办范围随流程自动回收。
  • 行级过滤统一写成公共 SQL 片段注入查询层,列表、详情、报表、导出走同一过滤,严禁用应用层循环筛选兜底。
  • 承办 ACL 由主办律师维护,协办与外聘的增删留痕,外聘账号强制 expire_at 并纳入自动失效巡检。
  • 按钮隐藏与后端接口做双重校验,perm_code 常量化管理,避免权限码字符串散落导致前后端口径不一致。
  • 密级字段按字段权限码脱敏返回,查看行为叠加审计与水印,杜绝截图外传后无法定位。
  • 审计日志只追加不修改,业务库与日志库分离,导出、删除、授权变更、密级查看全覆盖留痕。

技术总结

把本篇拆出的五层叠回一张全景图:功能权限管按钮与动作,数据范围管行集,承办 ACL 管案内协作与圈外访问,字段权限管敏感信息,审计日志管事后追责,五层共同构成律所案件管理系统的权限底座。若只做 RBAC 不做数据范围,"能点不能看"会退化成"全所皆可看";只做范围不做 ACL,则无法支撑一个案件多人协作的正常形态。验收阶段可用一条准则自测:用最小授权的临时账号逐一走越权路径------把助理查询改成他人案件 ID 直调接口、把过期外聘账号重放请求、把无导出权限的账号走导出接口------任一环节放行,都说明过滤尚未下沉到数据访问层。结论很直接:案件系统权限体系不是一张配置表,而是贯穿建模、查询、前端、审计全链路的工程约束,其实现质量决定了律所数据安全的下限。

相关推荐
Miss roro4 个月前
法律科技的发展脉络:从数字化管理到AI辅助办案的演进路径
大数据·人工智能·科技·法律科技·律所管理系统·案件管理系统
Miss roro4 个月前
法律文书信息自动提取:OCR识别与AI技术在案件管理中的应用
人工智能·ocr·法律科技·律所管理系统·案件管理系统