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 AppRead2024 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 NewPass2025;这里有一个很关键的点在KingbaseES以及PostgreSQL系里ALTER USER ... WITH PASSWORD之后的密码是明文写在SQL里的。这在交互式ksql会话里问题不大但如果你把这类语句写进自动化脚本脚本本身必须做好权限管控不能放在一个所有人可读的地方。修改用户其他属性的语法和创建时类似比如调整连接数限制ALTER USER app_read WITH CONNECTION LIMIT 50;禁用账号而不删除用NOLOGINALTER USER app_read NOLOGIN;这种方式比直接删掉更安全因为账号如果还拥有某些对象比如建了表直接删除会报依赖错误。先禁用、观察一段时间再删是更稳妥的流程。删除用户用DROP USER IF EXISTS app_read;注意如果这个用户名下还有表、视图、函数等对象或者它还持有某些对象的权限DROP USER会失败。这时要么先把对象转移给别人要么先回收权限要么用DROP OWNED BY app_read;把该用户拥有的对象和权限一并清理掉。DROP OWNED BY是个非常实用的清理命令但用的时候要极度小心它会删除该用户拥有的所有对象不可逆。2.3 用户管理的高频注意事项结合我踩过的坑整理几条用户管理阶段容易出问题的地方第一CREATE USER和CREATE 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_role、app_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后面会专门讲。序列的授权经常被忘记。使用SERIAL、BIGSERIAL或者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 Report2024; 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_role、read_write_role、etl_role顶层应用角色app_order_role、app_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_userarwdDxt/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脚本确认无误后再去生产环境执行这个习惯帮我避免了至少三次误回收权限的事故。