DBAPI 实现不同角色控制查看表的不同列
DBAPI 实现不同角色控制查看表的不同列
场景说明
在数据库管理系统中,对表进行列级别的权限控制是一项关键的安全措施,特别是在处理敏感数据或需要遵守特定数据访问控制策略的情况下。合理的列权限控制不仅能保护敏感信息,还能帮助组织满足合规性要求。
以下是一些典型的应用场景:
敏感数据保护
当表中包含敏感信息(如个人身份信息、财务数据、医疗记录等)时,通过列权限控制,只允许特定角色或用户访问这些列,可以有效防止数据泄露,降低数据安全风险。
合规性要求
某些行业或地区有严格的数据保护法规(如 GDPR、HIPAA 等),要求对敏感数据的访问进行严格控制。列权限控制可以帮助组织满足这些合规性要求,避免因数据泄露而面临的法律风险。
数据分层访问
在大型企业中,不同部门或用户角色可能只需要访问表中的部分列。通过列权限控制,可以确保用户只能访问其工作所需的列,实现最小权限原则,减少不必要的数据访问风险。
实现方案
DBAPI 提供了 API 编排、动态 SQL、数组传参的功能,通过这些功能,可以实现列权限控制。
为了实现列权限可以动态配置,我们采用以下方案:
- 动态权限配置:在数据库中维护一张权限配置表,用于配置不同用户可以访问的列,便于权限信息的动态修改。
- API 编排实现:创建两个独立的 API,并通过编排顺序执行。第一个 API 查询用户拥有的权限列,第二个 API 根据这些权限列查询表数据。
实施示例
1. 创建商品信息表
首先准备一张商品信息表 products
,包含以下列:
字段名 | 类型 | 说明 |
---|---|---|
id | int | 商品 ID |
name | varchar | 商品名称 |
description | text | 商品描述 |
category | varchar | 商品分类 |
cost | decimal | 进货成本(敏感数据) |
supplier | varchar | 供应商(敏感数据) |
示例数据如下:
id | name | description | category | cost | supplier |
---|---|---|---|---|---|
1 | iPhone 13 Pro | 高性能手机 | 手机 | 9999 | apple |
2 | MacBook Pro | 性能 Strong | 电脑 | 19999 | apple |
3 | Mate 60 | 高性能手机 | 手机 | 6999 | 华为 |
其中,商品的进货成本 cost
列和供应商 supplier
列为保密数据,需要根据用户的角色进行权限控制。
2. 创建权限配置表
创建权限配置表 permission_config
,用于记录用户对表列的权限信息:
字段名 | 类型 | 说明 |
---|---|---|
user_id | bigint | 用户 ID |
table_name | varchar | 表名 |
column_name | varchar | 列名 |
示例数据如下:
user_id | table_name | column_name |
---|---|---|
1 | products | cost |
1 | products | supplier |
2 | products | supplier |
3. 创建查询用户权限列的 API
创建 API,配置参数 userid
,类型为 bigint
,SQL 查询语句如下:
SELECT column_name FROM permission_config WHERE user_id = #{userid} and table_name = 'products'
该 API 用于获取指定用户有权访问的所有列名。
数据格式转换
配置完成后,我们进行请求测试,发现返回的数据格式为:
{"data": [{ "column_name": "cost" }, { "column_name": "supplier" }],"msg": null,"success": true
}
但实际上我们期望的数据格式是简洁的数组形式:["cost", "supplier"]
。
为了实现这一目标,我们需要对 API 返回的数据进行格式转换。在 API 配置中找到"全局插件 - 数据转换"选项,填写以下 Groovy 脚本:
data.data.collect { it.column_name }
这段 groovy 代码的意思是将返回的数据中的 data 属性进行循环, 提取每个元素的
column_name
字段,并返回一个包含这些提取值的数组。
并将脚本语言选择为 Groovy
:
配置完成后保存 API,并再次进行请求测试。可以看到返回的数据格式已经转换为我们期望的数组形式:
通过这样的数据转换处理,我们成功将原始的复杂 JSON 结构简化为便于后续处理的字符串数组格式,为下一步的数据查询操作提供了便利。
4. 创建查询表数据的 API
创建 API,配置参数 authedColumns
,类型为 Array<string>
,SQL 查询语句如下:
SELECT name, description,
<foreach collection="authedColumns" item="column" separator=",">${column}
</foreach>
FROM products
注意事项:
- 使用
<foreach>
标签遍历authedColumns
数组,将每个列名拼接成 SQL 语句中要查询的列名 - 必须使用
${column}
来动态替换列名,不能使用#{column}
请求测试 API,可以看到 API 正常返回数据:
5. 创建编排 API
按照以下步骤创建编排 API:
步骤 1:配置基础参数
创建编排 API,配置参数 userid
,类型为 bigint
:
步骤 2:添加开始节点
点击添加开始节点,作为流程的起点:
步骤 3:添加权限查询节点
- 点击添加 API 节点,编辑 API 节点,选择第一个 API(查询用户权限列)
- 配置参数
userid
为parameters.userid
- 配置节点 ID 为
getAuthedColumns
parameters
表示整个 API 的参数,parameters.userid
表示取参数 userid 的值
步骤 4:添加数据查询节点
- 点击添加 API 节点,选择第二个 API(查询表数据)
- 配置参数
authedColumns
为results.getAuthedColumns
- 配置节点 ID 为
getData
results
表示所有 API 节点的返回结果,results.getAuthedColumns
表示取第一个 API 的返回结果
步骤 5:添加结束节点
- 点击添加结束节点
- 配置数据脚本为
results.getData.data
results
表示所有 API 节点的返回结果,results.getData.data
表示取第二个 API 的返回结果中的 data 字段的值
步骤 6:连接节点
使用连线将所有节点按顺序连接起来:开始节点 → getAuthedColumns 节点 → getData 节点 → end 节点
最后点击保存并发布编排 API:
预期效果
通过以上配置,不同用户将看到不同的数据列:
- 用户 1:可以查看
cost
和supplier
列
- 用户 2:只能查看
supplier
列
这种实现方式既保证了数据的安全性,又提供了灵活的权限管理机制。
总结与优化
处理空列权限的 SQL 语法问题
对于第二个 API(查询表数据的 API),当某个用户没有可访问的敏感列权限时,会生成如下 SQL 语句:
SELECT name, description, FROM products
这个 SQL 语句存在语法错误(末尾多余的逗号),会导致查询执行失败。
解决方案
DBAPI 提供了类似 MyBatis 的动态 SQL 功能,其中的 trim
标签可以优雅地解决这个问题。修改后的 SQL 如下:
SELECT
<trim prefix="" suffix="" suffixesToOverride="," prefixesToOverride="">name, description,<foreach collection="authedColumns" item="column" separator=",">${column}</foreach>
</trim>
FROM products
trim 标签详解
trim
标签是 DBAPI 中一个强大的动态 SQL 处理工具,其主要属性包括:
prefix
:在包裹内容前添加指定前缀suffix
:在包裹内容后添加指定后缀suffixesToOverride
:指定需要去除的后缀字符prefixesToOverride
:指定需要去除的前缀字符
在这个场景中,我们将 suffixesToOverride
属性设置为逗号,
,表示当 trim
标签包裹的内容以逗号结尾时,会自动去除结尾的逗号,从而避免 SQL 语法错误。
这种动态 SQL 处理机制确保了无论用户拥有多少列权限,生成的 SQL 语句都是语法正确的,大大提高了系统的健壮性和用户体验。
根据客户端身份获取权限
基于用户ID参数获取权限存在安全风险,客户端可能通过修改userid
参数获取未授权的数据。更安全的做法是根据客户端身份(clientId)来确定数据访问权限。
1. 创建并配置客户端
首先创建两个客户端并授权其访问相应的API分组:
2. 调整权限配置表结构
修改权限配置表 permission_config
,使用client_id
替代user_id
来配置列权限:
字段名 | 类型 | 说明 |
---|---|---|
client_id | varchar | 客户端 ID |
table_name | varchar | 表名 |
column_name | varchar | 列名 |
示例数据:
client_id | table_name | column_name |
---|---|---|
CMlWBCb0v7U3goZG | products | cost |
CMlWBCb0v7U3goZG | products | supplier |
hTAOTQ5DmEb7IXMQ | products | supplier |
3. 更新权限查询API
调整权限查询API配置:
- 移除
userid
参数 - 将API权限设置为私有
- 更新SQL语句使用系统内置变量
__clientId
:
SELECT column_name FROM permission_config
WHERE client_id = #{__clientId} and table_name = 'products'
提示:SQL中可直接使用系统内置变量
__clientId
获取当前请求的客户端ID。
4. 验证权限控制效果
使用不同客户端访问API,会得到不同的数据列权限:
注意:访问私有API必须携带有效的token,系统通过token识别客户端身份。