摘要
這篇文章介紹了一個企業系統中的字段權限解析方案,通過規則表與命中記錄表(biz_rule_hit)聯動,實現對業務數據的動態權限控制。流程包括替換用戶上下文變量、記錄命中規則、查詢業務數據并關聯命中信息,最終在內存中合并字段權限等級(editable/view/masked/hidden),返回前端使用。該方案支持多規則疊加、權限優先級合并,并實現前后端解耦,適用于復雜權限場景的可審計、可擴展設計。
從規則表 + biz_rule_hit → JOIN 業務表 → 內存生成字段權限 → 返回前端,并且處理四種權限(editable/view/masked/hidden)逗號分割字段。
下面用 Java + 數據庫 的完整示例:
1?? 數據庫:統一規則命中表
CREATE TABLE biz_rule_hit (biz_table_name VARCHAR(128) NOT NULL, -- 業務表名biz_id INT NOT NULL, -- 業務表主鍵rule_id INT NOT NULL, -- 命中規則IDuser_id INT NOT NULL, -- 用戶IDhit_time DATETIME DEFAULT GETDATE(),PRIMARY KEY (biz_table_name, biz_id, rule_id, user_id)
);
- 一張表管理所有業務表的命中記錄
biz_table_name
區分業務表
2?? 讀取規則表(替換用戶上下文變量 + 解析字段)
class Rule {int ruleId;String evaluatedCondition; // 替換后的 SQL 條件List<String> editable;List<String> view;List<String> masked;List<String> hidden;
}int userId = 123;
int deptId = 10;
String bizTable = "biz_table";List<Rule> rules = jdbcTemplate.query("SELECT rule_id, condition_sql, editable, view, masked, hidden FROM rule_table WHERE biz_table = ? ORDER BY priority ASC",new Object[]{bizTable},(rs, rowNum) -> {Rule r = new Rule();r.ruleId = rs.getInt("rule_id");r.evaluatedCondition = rs.getString("condition_sql").replace("@UserID", String.valueOf(userId)).replace("@DeptID", String.valueOf(deptId));r.editable = Arrays.asList(rs.getString("editable").split(","));r.view = Arrays.asList(rs.getString("view").split(","));r.masked = Arrays.asList(rs.getString("masked").split(","));r.hidden = Arrays.asList(rs.getString("hidden").split(","));return r;}
);
3?? 寫入 biz_rule_hit
命中表
// 先刪除該用戶該業務表舊記錄
jdbcTemplate.update("DELETE FROM biz_rule_hit WHERE user_id = ? AND biz_table_name = ?", userId, bizTable);// 插入命中記錄
StringBuilder sqlInsertHits = new StringBuilder();
for(Rule rule : rules){sqlInsertHits.append("INSERT INTO biz_rule_hit (biz_table_name, biz_id, rule_id, user_id) ").append("SELECT '").append(bizTable).append("', b.id, ").append(rule.ruleId).append(", ").append(userId).append(" FROM ").append(bizTable).append(" b WHERE ").append(rule.evaluatedCondition).append(";");
}
jdbcTemplate.execute(sqlInsertHits.toString());
- 命中表存儲 業務表名 + biz_id + rule_id + user_id
- 可以長期保存,支持審計
4?? 查詢業務表 + JOIN 命中表
String sqlQuery = "SELECT b.*, r.rule_id " +"FROM " + bizTable + " b " +"LEFT JOIN biz_rule_hit r " +"ON r.biz_id = b.id AND r.biz_table_name = ? AND r.user_id = ?";List<Map<String,Object>> bizData = jdbcTemplate.queryForList(sqlQuery, bizTable, userId);
rule_id
為 NULL → 沒命中規則- LEFT JOIN 保證業務表所有行都返回
5?? 內存生成字段權限(合并四種權限等級)
Map<String, Integer> levelMap = Map.of("hidden", 0,"masked", 1,"view", 2,"editable", 3
);Map<Integer, Map<String, String>> bizPermissions = new HashMap<>();for(Map<String,Object> row : bizData){int bizId = (Integer) row.get("id");bizPermissions.putIfAbsent(bizId, new HashMap<>());Integer ruleId = (Integer) row.get("rule_id");if(ruleId == null) continue; // 沒命中規則// 找到規則對象Rule rule = rules.stream().filter(r -> r.ruleId == ruleId).findFirst().orElse(null);if(rule == null) continue;// 更新每個字段權限updatePermission(bizPermissions.get(bizId), rule.editable, "editable", levelMap);updatePermission(bizPermissions.get(bizId), rule.view, "view", levelMap);updatePermission(bizPermissions.get(bizId), rule.masked, "masked", levelMap);updatePermission(bizPermissions.get(bizId), rule.hidden, "hidden", levelMap);
}// 工具方法
void updatePermission(Map<String,String> permMap, List<String> fields, String newLevel, Map<String,Integer> levelMap){for(String field : fields){String current = permMap.getOrDefault(field, "hidden");if(levelMap.get(newLevel) > levelMap.get(current)){permMap.put(field, newLevel);}}
}
- editable > view > masked > hidden
- 多條規則疊加時取最大權限
6?? 返回前端
List<Map<String,Object>> result = bizData.stream().map(row -> {int bizId = (Integer) row.get("id");row.put("permissions", bizPermissions.getOrDefault(bizId, Collections.emptyMap()));return row;}).collect(Collectors.toList());
- 前端直接使用
permissions
控制字段顯示/編輯/脫敏 - 完全透明,不需要解析 SQL 規則
? 總結流程
- 查詢規則表 → 替換用戶上下文 → 解析字段名
- 寫入統一
biz_rule_hit
表 - 查詢業務表 + LEFT JOIN 命中表 → 得到命中規則
- 內存合并字段權限(四種等級)
- 返回前端
- 一張
_rule_hit
表管理所有業務表 - 支持用戶變量、權限等級合并、多條規則疊加
- 前端拿到
permissions
即可控制字段