KingbaseES用户与权限管理实战:从用户创建到角色授权与安全加固
2026/9/24 19:48:13 网站建设 项目流程

1. 内容整体设计与思路拆解

1.1 为什么用户与权限管理是KingbaseES安全控制的核心

KingbaseES作为一款关系型数据库管理系统,其安全体系可以从"谁进来、能干什么、能看到什么"三个维度去理解。"谁进来"对应身份认证,"能干什么"对应权限分配,"能看到什么"对应行级安全性、脱敏等高级特性。在这三个维度里,用户与权限管理是地基中的地基——认证做得再严格,如果用户建立之后权限失控,一个本该只有查询权限的账号却能删表,那所有上层安全机制都形同虚设。

我见过不少团队在使用金仓数据库时,习惯于把所有业务应用都用一个超级用户账号连接,理由是"反正内网环境,图省事"。这种做法在项目初期数据量小、团队人少的时候确实看不出什么问题,一旦业务上线、人员流动、审计要求提上来,就会变成噩梦:你不知道谁改过表结构,也没法单独收回某个应用或某个人的访问权,出了问题只能整个库一起排查。

所以我在做数据库规划时,第一件事永远是先把用户和权限的架子搭好。这个架子不需要一步到位完美无缺,但必须遵循一个基本原则:最小权限,按角色收口。也就是说,每个真实的人或应用只给够用的权限,不要多给;权限的分配尽量通过角色(ROLE)来批量管理,而不是逐个用户单独授权。这条原则贯穿这篇指南的所有操作。

1.2 ksql在用户与权限管理中的角色定位

ksql是KingbaseES自带的交互式命令行工具,功能上对应PostgreSQL生态里的psql。它不只是拿来跑SQL的客户端,更是DBA日常管理数据库的主阵地。创建用户、分配权限、查看权限归属、排查连接问题,这些操作在ksql里都能完成,而且很多操作比用图形化管理工具(比如KingbaseES自带的迁移与开发工具)更加直接、可控。

举个例子:你想快速看一个数据库上有哪些用户、各自是什么角色属性,在ksql里敲一条\du命令就能一目了然;想看某张表的权限分配,\dp 表名直接列出所有授权关系。这种高密度信息展示方式,是图形界面很难达到的。

另外,ksql脚本化能力也很强。你可以把创建用户、授权、初始化权限配置的SQL写成一个.sql脚本,用ksql -f init_security.sql一次性执行,新环境部署时就能做到权限配置的标准化和可回溯。这一点在后文会有具体演示。

2. 用户管理的核心操作与底层逻辑

2.1 创建用户:语法拆解与密码策略

KingbaseES中创建用户的标准语句是CREATE USER,它和CREATE ROLE的区别只有一个:CREATE USER默认带LOGIN属性,也就是允许该账号登录数据库;而CREATE ROLE默认不带LOGIN,通常用来充当权限组。先看一个最典型的建用户语句:

CREATE USER app_read WITH PASSWORD 'App@Read2024' VALID UNTIL '2025-12-31 23:59:59' CONNECTION LIMIT 20;

这条语句做了四件事:创建名为app_read的登录账号、设置密码、设置密码有效期、限制最大连接数。我建议在实际生产环境里,密码、有效期、连接数限制这三项都要显式指定,而不是依赖缺省值。

密码复杂度方面,KingbaseES默认可能只校验非空,并不会强制要求大小写字母、数字、特殊字符的组合。如果你所在的项目有等保或行业合规要求,建议打开密码复杂度校验插件。在ksql里可以这样确认:

SELECT name, setting FROM pg_settings WHERE name LIKE 'password%';

如果输出里没有passwordcheck相关的加载项,说明密码复杂度策略没有生效。这种情况下,只能在业务层面约束:所有数据库账号的密码长度不低于12位,必须包含大写字母、小写字母、数字和特殊字符。我在实际项目中就遇到过因为密码策略太宽松,一个测试账号被暴力撞库成功的案例,所以这块千万别偷懒。

还有一个容易忽视的细节:VALID UNTIL这个参数。很多团队建账号的时候不设置有效期,用户离职后账号就永远躺在那里。等审计来查的时候,发现一个三年前离职的人还有个数据库账号能登录,这是非常尴尬的事。我的习惯是:所有临时账号、外包人员账号、短期项目账号,一律设置VALID UNTIL,到期自动失效,省去人工清理的麻烦。

2.2 修改与删除用户:ALTER USER和DROP USER的正确姿势

用户建立之后,调整配置是常有的事。改密码是最常见的操作:

ALTER USER app_read WITH PASSWORD 'NewPass@2025';

这里有一个很关键的点:在KingbaseES(以及PostgreSQL系)里,ALTER USER ... WITH PASSWORD之后的密码是明文写在SQL里的。这在交互式ksql会话里问题不大,但如果你把这类语句写进自动化脚本,脚本本身必须做好权限管控,不能放在一个所有人可读的地方。

修改用户其他属性的语法和创建时类似,比如调整连接数限制:

ALTER USER app_read WITH CONNECTION LIMIT 50;

禁用账号而不删除,用NOLOGIN

ALTER USER app_read NOLOGIN;

这种方式比直接删掉更安全,因为账号如果还拥有某些对象(比如建了表),直接删除会报依赖错误。先禁用、观察一段时间再删,是更稳妥的流程。

删除用户用:

DROP USER IF EXISTS app_read;

注意:如果这个用户名下还有表、视图、函数等对象,或者它还持有某些对象的权限,DROP USER会失败。这时要么先把对象转移给别人,要么先回收权限,要么用DROP OWNED BY app_read;把该用户拥有的对象和权限一并清理掉。DROP OWNED BY是个非常实用的清理命令,但用的时候要极度小心,它会删除该用户拥有的所有对象,不可逆。

2.3 用户管理的高频注意事项

结合我踩过的坑,整理几条用户管理阶段容易出问题的地方:

第一,CREATE USERCREATE ROLE别混用。如果你的本意是建一个可登录的业务账号,却用了CREATE ROLE,那么这个账号在客户端连接时会报 "role does not exist" 或者权限不足,因为角色默认没有LOGIN属性。反过来,如果你想建一个纯粹的权限组,用了CREATE USER,它默认就能登录,这又多了一个可登录的账号,偏离最小权限原则。

第二,密码存储在数据库中是加密的,不要试图去系统表里查密码明文。KingbaseES对密码有专门的安全存储机制,以密文形式保存在系统目录中。如果你发现自己能看到其他用户的密码,那说明配置有大问题。

第三,不要轻易给应用账号设置SUPERUSER属性。我在排查故障时见过太多因为图省事给应用账号赋了超管权限的情况,一旦应用被SQL注入,数据库等于全裸。应用账号只需要它真实需要的库表权限就够了。

3. 权限体系解析与授权操作

3.1 KingbaseES权限模型:系统权限、对象权限与角色

KingbaseES的权限模型可以简单分成三层:

  • 系统权限(属性权限):比如能否登录(LOGIN)、能否创建数据库(CREATEDB)、能否创建角色(CREATEROLE)、是否超级用户(SUPERUSER)。这类权限是账号级别的属性,用ALTER USER来调整。
  • 对象权限:对具体对象(表、视图、序列、函数、模式等)的增删改查、执行等权限。数据库内核通过内存中的权限位图来判断,而用户查看权限归属通常查系统视图。
  • 角色权限:把一个角色授予另一个用户或另一个角色,从而实现权限的批量继承和传递。

很多人刚接触金仓时会把"角色"和"用户"割裂开理解,其实在KingbaseES里两者是统一的:用户本质上就是带LOGIN属性的角色。你可以把"角色"理解成一个权限集合的容器,用户可以把它授予别的用户。

从管理角度来看,我推荐的做法是:先建立业务角色(比如app_read_roleapp_write_role),再把角色授予具体的用户账号。这样当权限策略调整时,只需要改角色的权限,所有继承该角色的用户会同步生效,不需要逐个用户去授权修改。

3.2 GRANT授权实操:从库级到表级的完整路径

授权语句的基本格式是:

GRANT <权限列表> ON <对象类型> <对象名> TO <用户或角色>;

先说对象权限,最常见的是对表的授权:

-- 给app_read角色授予public模式下所有现有表的查询权限 GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_read_role; -- 给app_write角色授予public模式下现有表的增删改查权限 GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_write_role;

这里要注意ON ALL TABLES IN SCHEMA只对当前已存在的表生效。如果以后要在该模式下新建表,新表并不会自动带上这些授权。要解决这个问题,需要用到默认权限(ALTER DEFAULT PRIVILEGES),后面会专门讲。

序列的授权经常被忘记。使用SERIALBIGSERIAL或者nextval的场景下,如果用户没有序列的USAGE权限,插入数据时会报 "permission denied for sequence"。所以授权时要把序列也带上:

GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_write_role;

模式权限之前,还有个前提:用户需要拥有模式的USAGE权限,才能访问该模式下的对象。否则即使表权限授了,连进模式都会报权限不足:

GRANT USAGE ON SCHEMA public TO app_read_role; GRANT USAGE ON SCHEMA public TO app_write_role;

给函数授权则用EXECUTE。默认情况下,函数对PUBLIC是开放EXECUTE权限的(即所有角色均可执行)。从安全角度思考,如果项目对安全要求较高,可以考虑回收函数的公共执行权限,只给特定角色:

REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO app_write_role;

数据库级权限、表空间级权限在大型项目里也可能用到,但日常业务场景中,把模式、表、序列、函数这四个维度的权限理清楚,已经能覆盖绝大多数需求。

3.3 REVOKE回收权限:谨慎操作的细节

回收权限用REVOKE,语法是授权的镜像:

REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM app_read_role; REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM app_write_role;

需要注意几点:

  • 权限回收只影响之后发起的新SQL。如果某个应用连接池里已经有空闲连接,连接里的会话状态可能仍持有旧权限信息,极端情况下需要等连接池回收重建才完全生效。遇到"明明回收了权限还能操作"的情况,先检查是不是连接池没刷新。
  • PUBLIC是一个特殊角色,表示所有用户。REVOKE ... FROM PUBLIC的操作面非常大,执行前务必反复确认。
  • 如果用户是通过角色继承获得的权限,你需要回收的是"角色被授予"这个关系,而不是去用户身上单独处理:
REVOKE app_write_role FROM app_user;

3.4 用默认权限解决"新表没授权"的坑

刚才提到ON ALL TABLES只覆盖已存在的表,新建的表不会自动带权限,这是权限管理中最容易埋雷的点。如果每次建表都要手动补授权,一定会有遗漏。解决办法是用ALTER DEFAULT PRIVILEGES设置默认权限:

-- 指定在public模式下,为app_write_role授予未来新建表的增删改查权限 ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_write_role; -- 同样给app_read_role授予未来新建表的查询权限 ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app_read_role;

设置好之后,只要是用创建该默认权限设置的属主角色创建的新表,就会自动带上对应授权。这一点我建议所有使用金仓数据库的项目组都在初始化时就配好,省去后续大量补授权的重复劳动。

需要注意的是,ALTER DEFAULT PRIVILEGES的设置是和"执行该语句的角色"绑定的。也就是说,你用哪个用户执行了这条语句,未来只有该用户建的表才会自动应用这些默认权限。如果业务系统有不同的建表账号,需要在各自的账号下都做一遍设置。

4. 角色管理与安全加固实践

4.1 创建角色:把权限收口到角色上

建议不要在用户上直接挂一堆对象权限,而是通过角色中转。创建角色的语句很简单:

CREATE ROLE app_read_role NOLOGIN; CREATE ROLE app_write_role NOLOGIN;

授予角色权限、再把角色授予用户:

-- 给角色授权 GRANT USAGE ON SCHEMA public TO app_read_role; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_read_role; -- 创建用户并授予角色 CREATE USER app_report WITH PASSWORD 'Report@2024'; GRANT app_read_role TO app_report; -- 如果用户已经存在,直接补授角色 GRANT app_read_role TO existing_user;

这种做法的好处非常明显。假设公司来了三个数据分析师,他们都只需要只读权限。如果不走角色,你需要分别给三个用户授权,而且授权内容还可能因为手抖不一致。走角色的化,一次给角色授权,三个用户继承角色即可。将来某个用户离职,回收角色比逐条回收权限简单得多。

4.2 角色继承与多层角色设计

KingbaseES的角色可以形成继承链:角色A授予角色B,角色B授予用户C,那么C就拥有A和B的所有权限。这在复杂组织架构里很有用。

举个例子,一个典型的业务库可能有三种角色层级:

  • 底层基础角色:base_conn_role(所有业务账号都必须要的连接和会话权限)
  • 中层职能角色:read_only_roleread_write_roleetl_role
  • 顶层应用角色:app_order_roleapp_pay_role

然后用中层角色去授权,再把中层角色合并到顶层应用角色,最后把顶层角色授予具体用户。这样设计的好处是,权限变更时只需要改动一个节点,波及面可控。

不过我不建议把继承链拉得太深。层数太多以后,排查"这个用户的权限到底从哪来的"会非常痛苦。在ksql里查看角色继承关系时,信息是分散的,需要靠多个系统视图联查。所以我的建议是:角色层级控制在三层以内,超过三层就应该考虑简化。

4.3 安全加固技巧:从权限角度防护数据库

建完用户和角色以后,还有几个安全加固的点,是在权限分配之外容易被忽略的:

回收public模式的CREATE权限。PostgreSQL系数据库默认情况下,任意用户都可以在public模式下创建对象。如果团队内部使用还好,如果库里有多个应用共用,或者有外部人员的数据分析账号,最好把public模式的创建权限收掉:

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

限制超级用户的使用场景。日常业务操作,一律用普通用户+角色授权来完成。超级用户只保留给DBA做运维操作时使用,而且要开启审计日志,记录超级用户的所有操作。

定期审查无效账号。我习惯每个月用一条SQL看看库里有没有长期不登录的账号:

SELECT rolname, rolcanlogin, rolsuper, rolconnlimit FROM pg_roles ORDER BY rolname;

再结合数据库日志或审计记录,找出哪些账号超过90天没有登录,逐个确认是否还存在。

最小化对外暴露的数据库对象。能查到的前提是能被访问到,如果业务上不需要某个视图、某个函数对外暴露,就不要给它授权。权限只开当前需要的口子,不开未来的口子。

5. 查看权限信息与问题排查实录

5.1 用系统视图和ksql元命令快速掌握权限全景

实际操作中,我需要快速回答几类问题:"库里有哪些用户?""某个用户能访问哪些表?""某张表被授权给了谁?"。回答这些问题主要靠两类途径:ksql元命令和系统视图查询。

ksql元命令里最常用的是:

  • \du:列出所有角色/用户,以及它们的属性(超级用户、创建数据库、复制等)。
  • \du+:更详细的角色信息,包括角色成员关系。
  • \dp 表名或者\z 表名:查看表的权限分配情况。
  • \l:列出数据库及其属主。
  • \dn:列出所有模式。

\dp的输出里,每一行代表"某个对象被授予了何种权限给哪些角色"。格式类似db_user=arwdDxt/user这种缩写串,每个字母对应一种权限:r是SELECT、w是UPDATE、a是INSERT、d是DELETE、t是TRUNCATE、x是REFERENCES、D是TRIGGER。第一次看可能有点懵,习惯之后会觉得这个展示方式非常紧凑高效。

如果需要更灵活地过滤,可以直接查系统视图。我在实际工作中最常用的查询是:

-- 查看某个用户被授予的所有角色 SELECT r.rolname AS grantee, a.rolname AS granted_role FROM pg_auth_members m JOIN pg_roles a ON m.roleid = a.oid JOIN pg_roles r ON m.member = r.oid WHERE r.rolname = 'app_report';
-- 查看某张表的权限分配 SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name = 'orders' ORDER BY grantee, privilege_type;

这两类查询基本覆盖了"查用户有哪些角色"和"查表授权给了哪些角色"两个高频需求。

5.2 常见权限问题速查表与排查思路

我在日常运维里总结了一张权限问题速查表,碰到类似报错可以快速定位方向:

错误信息常见原因排查思路
permission denied for schema xxx用户没有该模式的USAGE权限GRANT USAGE ON SCHEMA xxx TO 用户/角色
permission denied for table xxx用户没有该表的对应权限检查\dp输出,确认授权是否在正确的角色路径上
permission denied for sequence xxx用户没有序列的USAGE权限补授序列权限,或者在授权时连带序列一起处理
role "xxx" does not exist登录账号不存在,或者建的是NOLOGIN角色检查用户名拼写,确认是不是忘了给角色加LOGIN属性
password authentication failed for user "xxx"密码错误,或者密码策略校验不通过重置密码,注意大小写和特殊字符
sorry, too many clients already连接数达到上限检查CONNECTION LIMIT设置和连接池配置

具体到排查"用户能访问哪些权限"时,最容易出问题的地方是:用户表面上像是有权限,但权限是通过角色继承的,而继承链条上有断点

我经历过一个真实案例:给某个报表账号授了app_read_role角色,应用端依然报查询权限不足。排查后发现,app_read_role只被授予了SELECT权限,但报表SQL里用到了某个视图,该视图引用了多张底层表。用户对底层表并没有SELECT权限,因为视图的访问还可能涉及到底层表的权限校验。解决方法是把底层表的查询权限也授权到对应角色上。

这种"视图套表"的权限问题在报表场景里非常常见,排查路径一般是:查看报错SQL涉及哪些对象,再逐个确认这些对象及其依赖对象的权限,不要只看报错的那一层对象。

5.3 我用过比较顺手的权限巡检脚本

最后分享一个我每季度都会跑一次的权限巡检思路。它不算复杂,但能把整个库的权限全貌拉出来过一遍:

-- 1. 列出所有登录用户及其角色属性 SELECT rolname, rolsuper, rolcreatedb, rolcanlogin, rolconnlimit, rolvaliduntil FROM pg_roles WHERE rolcanlogin = true ORDER BY rolname; -- 2. 列出所有角色及其成员关系 SELECT r.rolname AS role_name, m.rolname AS member_name FROM pg_auth_members a JOIN pg_roles r ON r.oid = a.roleid JOIN pg_roles m ON m.oid = a.member ORDER BY r.rolname, m.rolname; -- 3. 查看schema数量及属主 SELECT nspname, nspowner::regrole FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname <> 'information_schema' ORDER BY nspname;

把这几个查询的结果保存下来,每季度对比一次,就能及时发现"多了哪些用户""谁的角色变了""有没有新schema没人管"这些变化。对于没有专职DBA的团队来说,这算是一个成本很低但效果很好的权限体检方案。

我个人在实际操作中的体会是:用户和权限管理没有银弹,核心是养成"最小权限+角色收口+定期巡检"这三个习惯。金仓数据库的ksql工具本身做得已经比较顺手,日常管理只要把上面这些常用操作练熟,应对大部分业务场景都够用了。最后再分享一个小技巧:每次做权限变更之前,先在测试环境跑一遍ksql -f脚本,确认无误后再去生产环境执行,这个习惯帮我避免了至少三次误回收权限的事故。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询