🐘Postgresql用户权限体系

前言

写这篇文章的起因是近期碰到了需要与其他同事共同使用同一个数据库实例中不同数据库 的情况,之前都是大家直接使用 postgres 管理员账户,出于安全性考虑,避免其他同事动到不属于自己项目的数据库,所以想创建多个账户,这些账户分别拥有对应的数据库权限,而对其他数据库没有操作权限。

在创建这些账户的过程中,碰到了一些问题,所以深入的去了解了下 PG 的用户权限体系设计。

PostgreSQL 的权限和角色体系初看显得零散、层级多且概念交叉,感觉缺乏统一的逻辑,看官方文档和教材也有点云里雾里,通过一些文档和 DeepSeek,撰写了这篇文章。

一、角色(Role)管理

角色是 权限的载体,PostgreSQL 使用角色统一管理用户和组,角色可以拥有登录权限(即用户)或仅用于权限分组(即组)。

1. 理解角色和权限的本质

PostgreSQL 中只有角色,用户和组都是角色的变体。用户本质是带有登录权限的特殊角色。

PostgreSQL8.1 版本之前,用户和组是完全不同的两种实体,但是现在只有角色。

我们可以通过下边这张图来理解 PG 中角色的本质:

  • 角色本质上是权限的载体;
  • 角色和用户本质是同一个东西,只是创建语法不同(CREATE ROLE vs CREATE USER),用户实际上是带登录权限的特殊角色
  • 权限通过两种途径授予:直接对象授权和角色之间授权(继承)。
  • 继承本质是权限组合而非父子关系,当角色A被授予角色B时,A就获得了B的所有权限。

2. 创建角色

sql 复制代码
-- 创建普通角色(无登录权限)
CREATE ROLE group_developer;

-- 创建登录角色(用户)并设置密码
CREATE ROLE app_user WITH LOGIN PASSWORD 'password';

-- 创建超级用户(拥有所有权限)
CREATE ROLE super_user WITH SUPERUSER;

3. 角色属性

  • LOGIN:允许角色登录数据库(即用户)。
  • SUPERUSER:超级用户可绕过所有权限检查,创建 / 删除数据库、角色等。
  • CREATEDB:允许创建数据库。
  • CREATEROLE:允许创建和修改其他角色。
  • INHERIT:默认继承父角色的权限(可显式关闭)。
  • VALID UNTIL:角色有效期(如 VALID UNTIL '2024-12-31')。

3. 角色权限继承

sql 复制代码
-- 将角色添加到组(授予组权限)
GRANT group_developer TO app_user;

-- 撤销角色继承
REVOKE group_developer FROM app_user;

二、权限的层级结构

PostgreSQL 的权限支持不同的粒度,从数据库实例到具体的某个数据表,都可以设置权限。

⚠️注意拥有了某个数据库的权限,但并不意味着自动拥有库内所有对象的权限! 它拥有的是数据库本身的管理权。要操作库内对象(如表),还需显式授予该对象的权限。

1. 实例/集群级别

实例/集群级别权限,会影响整个 PostgreSQL 实例的操作。

  • CREATEDB: 允许创建数据库。
  • CREATEROLE: 允许创建/管理其他角色。
  • REPLICATION: 允许流复制。
  • SUPERUSER: 最高权限(绕过所有权限检查,极其危险,慎用)。

授予方式:

sql 复制代码
ALTER ROLE ... [CREATEDB | CREATEROLE | ...]

2. 数据库级别权限

针对单个数据库的权限。

  • CONNECT: 允许角色连接到该数据库(必备)。
  • CREATE: 允许在数据库的默认模式(通常是 public)中创建新对象(表、视图等)。注意: 在其他模式中创建对象需要该模式的 CREATE 权限。
  • TEMPORARY: 允许在数据库中创建临时表。
  • 授权方式:GRANT ... ON DATABASE ... TO ...
sql 复制代码
-- 授予连接数据库权限
GRANT CONNECT ON DATABASE mydb TO app_user;

-- 允许用户在数据库中创建模式
GRANT CREATE ON DATABASE mydb TO app_user;

2. 模式(Schema)级别权限

针对单个模式的权限。

  • USAGE: 允许角色访问模式中的对象(查找、引用)。
  • CREATE: 允许在模式中创建新对象。
  • 授予方式: GRANT ... ON SCHEMA ... TO ...
sql 复制代码
-- 授予模式访问权限
GRANT USAGE ON SCHEMA public TO app_user;

-- 允许在模式中创建对象
GRANT CREATE ON SCHEMA public TO app_user;

3. **对象级别**权限

对象级别权限包含表、视图、序列、函数、过程等 ,是最细粒度的权限。

3.1 表级别

  • SELECT:读取表数据。
  • INSERT:插入新数据。
  • UPDATE:更新数据(可指定列,如 UPDATE (col1, col2))。
  • DELETE:删除数据。
  • TRUNCATE:清空表数据(需谨慎,比 DELETE 更高效)。
  • REFERENCES:允许在该表上创建外键约束(针对列)。
  • TRIGGER:允许在表上创建触发器。
  • EXECUTE:对函数表(物化视图)的执行权限。
sql 复制代码
-- 授予表读写权限
GRANT SELECT, INSERT, UPDATE, DELETE ON table employees TO app_user;

-- 仅允许更新特定列
GRANT UPDATE (salary) ON table employees TO app_user;

-- 允许在表上创建外键
GRANT REFERENCES (id) ON table employees TO app_user;

3.2 序列(Sequence)权限

  • USAGE:允许使用序列的 NEXTVALCURRVAL 函数。
  • CREATE:允许修改序列(如重置值)。
  • SELECT:允许查询序列的当前值(CURRVAL)。
sql 复制代码
GRANT USAGE, SELECT ON SEQUENCE user_id_seq TO app_user;

3.3 函数(Function)权限

  • EXECUTE:允许调用函数。
sql 复制代码
GRANT EXECUTE ON FUNCTION calculate_salary() TO app_user;

3.4 索引(Index)权限

  • 索引权限与表权限绑定,创建索引的用户自动拥有索引权限,且无法单独授予索引权限(通过表权限间接控制)。

三、 Owner 的特殊性

PostgreSQL 中的每个数据库对象(数据库、模式、表、视图、函数等)都有一个拥有者 Owner。拥有者会自动拥有该对象的所有权限(即 ALL 权限),并且可以更改或删除该对象。拥有者可以将自己拥有的对象上的权限授予其他角色 (GRANT ...),也可以撤销 (REVOKE ...)。

🏷****修改数据库的 Owner:

创建对象的角色自动成为其拥有者,那么**谁可以更改数据库对象的 owner 呢?**对于数据库和表空间(Tablespace)对象来说,只有管理员(superuser)可以修改,而对于模式、表/视图/函数等对象,当前对象的 Owner 可以进行修改。

⚠️ 但需要注意的是 owner 的转移,并不等同于权限继承。

例如:把某个数据库 owner 从角色 A 修改为了角色 B,但该数据库下模式和表对象的 owner 依然是 A,用户 B 仍需被授予模式和表对象的权限才能访问。

不同对象类型的修改权限和命令示例如下:

对象类型 谁可以设置/更改 Owner SQL 命令示例
数据库 (Database) 超级用户 (Superuser) ALTER DATABASE db_name OWNER TO new_owner;
模式 (Schema) 超级用户 当前模式 Owner ALTER SCHEMA schema_name OWNER TO new_owner;
表/视图/序列 超级用户 当前对象 Owner ALTER TABLE table_name OWNER TO new_owner;
函数 (Function) 超级用户 当前函数 Owner ALTER FUNCTION func_name() OWNER TO new_owner;
表空间 (Tablespace) 仅超级用户 ALTER TABLESPACE tspace_name OWNER TO new_owner;

四、默认权限

默认权限(Default Privileges)允许你为将来创建的对象预先设置权限规则,从而避免每次创建新对象后都要手动授权的麻烦。

⚠️默认权限不适用于已经存在的对象,只影响设置之后新创建的对象。

**👉**如何配置默认权限:

使用 SQL设置新创建对象的默认权限(需在会话或数据库级别配置):

sql 复制代码
-- 对未来创建的表,授予 group_developer 读写权限
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO group_developer;

PgAdmin 中配置默认权限:

五、预定义角色

预定义角色是一种特殊的角色,由系统预先创建好并赋予了特定的权限,用于简化跨数据库的批量授权,避免为每个库、每个模式、每个对象单独授权。

🏷PG 中预定义角色列表:

角色名称 权限范围 适用场景
pg_read_all_data 所有表/视图的SELECT + 所有序列的USAGE + 所有模式的USAGE 监控/报表系统
pg_write_all_data 所有表/视图的INSERT/UPDATE/DELETE ETL工具账户
pg_read_all_settings 读取所有配置参数(pg_settings) 监控系统
pg_read_all_stats 读取所有统计视图(pg_stat_activity等) DBA监控
pg_stat_scan_tables 执行统计信息收集操作(ANALYZE) 维护任务
pg_monitor 组合:read_all_stats + stat_scan_tables + pg_stat_activity的读取权限 数据库监控
pg_signal_backend 向其他后端发送信号(取消查询、终止会话) 运维管理
pg_execute_server_program 执行服务器端程序(COPY FROM PROGRAM等) 备份/ETL

六、权限操作语法

1. 授予权限(GRANT)

sql 复制代码
-- 授予对象权限给角色
GRANT {权限列表} ON {对象} TO {角色 | PUBLIC};

-- 示例:授予表 employees 的 SELECT 权限给角色 app_user
GRANT SELECT ON employees TO app_user;

-- 授予模式 public 的所有权限给角色 group_developer
GRANT ALL PRIVILEGES ON SCHEMA public TO group_developer;

-- 授予权限时包含子对象(需 PostgreSQL 10+)
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_user;

2. 撤销权限(REVOKE)

sql 复制代码
REVOKE {权限列表} ON {对象} FROM {角色 | PUBLIC};

-- 示例:撤销 app_user 的 INSERT 权限
REVOKE INSERT ON employees FROM app_user;

-- 撤销所有权限
REVOKE ALL PRIVILEGES ON employees FROM app_user;

3. 查看权限

  • \z(psql 命令):查看表 / 视图权限。
plain 复制代码
psql -d mydb -c "\z employees"
  • 系统视图:
sql 复制代码
-- 查看表权限
SELECT * FROM information_schema.table_privileges WHERE table_name = 'employees';

-- 查看角色属性
SELECT * FROM pg_roles WHERE rolname = 'app_user';

七、权限检查流程

PostgreSQL 的权限检查是一个分层、逐级验证的过程,当用户尝试执行任何操作时,系统会按照严格的顺序进行权限验证。

  1. 角色存在性和登录权限
  2. 超级用户检查:如果是超级用户,跳过检查,直接允许操作,普通用户进入下一级检查
  3. 实例级权限:对于某些全局操作(如创建数据库、创建角色等),系统会检查角色是否具有相应的实例级权限(如CREATEDB、CREATEROLE 等)。
  4. 数据库连接权限 :检查是否具有该数据库的CONNECT权限
  5. 模式级权限 :在数据库中执行操作(如查询表)时,如果操作涉及特定模式(Schema)中的对象,系统会检查:用户是否具有该模式的USAGE权限。如果没有,即使对表有权限,也无法访问该模式中的对象。对于在模式中创建对象的操作(如创建表),需要模式的CREATE权限。
  6. 表级权限:当操作具体对象(如表、视图、序列、函数等)时,系统检查用户是否具有该对象的相关操作权限。
  7. 列级权限 :对于某些操作(如UPDATEREFERENCES),可以细化到列级别。例如,可以授予用户对表中特定列的UPDATE权限。

权限检查流程也解释了为什么当我们拥有某个数据表的权限,没有相应模式的 USAGE 权限,也无法访问这个数据表。

八、如何创建一个拥有单一数据库权限的角色

最后回到我们一开始的需求上,如何创建一个拥有某个数据库权限的角色呢?通过前边的只是,我们了解到,最简单的方法就是设置数据库的 Owner。 使用 PgAdmin 来实践下,

  1. 创建一个具有登录权限的用户
  1. 新建数据库,并设置 owner 为刚创建的用户
  1. 在创建数据表时候,设置数据表的 owner 为刚创建的用户。(如果使用的是这个用户创建数据表,则默认 owner 就是这个用户)

总结

PostgreSQL 的权限体系设计是分层级、基于角色、遵循最小权限原则的。看似复杂的概念(预定义角色、拥有者、对象权限)都是围绕这个核心设计展开的:

  • 角色 (ROLE) 是权限的容器和分配目标。
  • 权限 (GRANT) 按作用域层级(实例 -> 数据库 -> 模式 -> 对象)进行授予。
  • 拥有者 (OWNER) 对其拥有的对象拥有隐式完整权限,是权限管理的基础点。
  • 预定义角色是系统提供的、用于简化特定全局权限管理的特殊角色。
  • 角色成员关系和 WITH ADMIN OPTION 实现了权限的继承和委派。
相关推荐
奈斯ing25 分钟前
【MySQL篇】高效学习官方文档指南(基于MySQL8.0版本详解)
运维·数据库·学习·mysql
Brandon汐1 小时前
数据库part2---子查询
数据库·sql
Tapdata 钛铂数据1 小时前
信创 CDC 实战|国产数据库的数据高速通道:OceanBase 实时入仓 StarRocks
数据库·oceanbase
liyongjie1 小时前
openGauss数据库DWR报告解读
数据库·oracle
楼台的春风2 小时前
【Linux驱动开发 ---- 4.1_sysfs 详解】
linux·运维·c语言·数据库·人工智能·驱动开发·嵌入式硬件
一个天蝎座 白勺 程序猿3 小时前
深度体验KingbaseES在线平台:从零掌握企业级数据库实战(附架构图+代码案例)
数据库·k8s
傲祥Ax3 小时前
数据库中间件ShardingSphere5
数据库
码农开荒路3 小时前
Redis之缓存一致性
数据库·redis·缓存
篱笆院的狗4 小时前
Spring Boot 工程启动以后,我希望将数据库中已有的固定内容,打入到 Redis 缓存中,请问如何处理?
数据库·spring boot·缓存