# 角色与 GRANT

> 向对象授予权限，并使用普通数据库角色演示行级安全。

---

LLMS 索引： [llms.txt](/llms.txt)

---

PostgreSQL 用一个统一的概念——*角色*——表示用户和组。角色可以登录（使用 `LOGIN`），可以被授予给其他角色（实现类似组的行为），可以拥有对象，也可以获得授予给它的权限。`GRANT` 添加权限；`REVOKE` 移除权限。每个数据库对象都有一个拥有全部权限的所有者；其他人都需要获得明确的授权。这里的原则是*最小权限*：每个角色只获得它所需的权限。

## 本章要点 {#learn}

- 创建带登录和不带登录的角色
- 在模式、表和列上授予与撤销权限
- 模式上的 `USAGE` 权限（经常被忘记）
- 为将来创建的对象设置 `DEFAULT PRIVILEGES`
- 行级安全（RLS）简介

## 示例 {#example}

```sql {num="55.1" caption="角色与 GRANT"}
-- Inspect the current session
SELECT current_user, session_user;

-- Create ordinary roles (requires CREATEROLE or superuser privileges)
-- CREATE ROLE app_read NOLOGIN;                                 -- a "group" role
-- CREATE ROLE alice LOGIN PASSWORD 'change-me' IN ROLE app_read;
-- CREATE ROLE app_write NOLOGIN;

-- Grant access to a schema and read on tables
-- GRANT USAGE  ON SCHEMA public TO app_read;
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_read;

-- Future tables in this schema also get SELECT for app_read
-- ALTER DEFAULT PRIVILEGES IN SCHEMA public
--   GRANT SELECT ON TABLES TO app_read;

-- Write-level
-- GRANT app_read TO app_write;                          -- inheritance: app_write also reads
-- GRANT INSERT, UPDATE, DELETE ON fruits TO app_write;

-- Revoke
-- REVOKE INSERT ON fruits FROM app_write;

-- Inspect your privileges on a table
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'fruits' AND grantee = current_user;

-- Row-level security: per-row visibility decided by a policy
-- (demo on a separate table so we don't change `fruits` permissions)
DROP TABLE IF EXISTS tenant_docs;
CREATE TABLE tenant_docs (
  id       integer PRIMARY KEY,
  tenant   text NOT NULL,
  content  text NOT NULL
);
INSERT INTO tenant_docs VALUES (1, 'acme', 'hello'), (2, 'globex', 'world');

ALTER TABLE tenant_docs ENABLE ROW LEVEL SECURITY;
-- Apply the policy to the table owner as well (superusers/BYPASSRLS still bypass it).
ALTER TABLE tenant_docs FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON tenant_docs
  USING (tenant = current_setting('app.tenant', true));

-- Set the tenant for the session and try a select
SET app.tenant TO 'acme';
SELECT * FROM tenant_docs;        -- only sees the 'acme' row

SET app.tenant TO 'globex';
SELECT * FROM tenant_docs;        -- only sees the 'globex' row

-- Cleanup
DROP POLICY IF EXISTS tenant_isolation ON tenant_docs;
DROP TABLE IF EXISTS tenant_docs;
```

表权限包括 `SELECT`、`INSERT`、`UPDATE`、`DELETE`、`TRUNCATE`、`REFERENCES`、`TRIGGER`，以及 PostgreSQL 17 中的 `MAINTAIN`。模式上最重要的权限是 `USAGE`（允许角色引用其中的对象）和 `CREATE`（允许角色创建新对象）。数据库上的 `CONNECT`、`CREATE` 和 `TEMPORARY` 也很重要。

表上的权限不会从模式级授权中继承。要使用模式 `public` 中的表，角色需要在 `public` 上拥有 `USAGE`，并且在该表上拥有某项权限（例如 `SELECT`）。缺少模式级 `USAGE` 时，就会出现常见的“permission denied for schema public”错误。

`DEFAULT PRIVILEGES` 解决了“新表没有授权”的问题：角色以后创建的任何对象都会自动继承配置好的授权。这些配置只适用于指定的创建角色所创建的对象，不会自动适用于每个角色创建的对象。对于会创建表的每个迁移/所有者角色，都要分别进行配置。

行级安全（RLS）是一个更高级的功能：你可以在表上启用 `ENABLE ROW LEVEL SECURITY`，并编写根据谓词过滤行的 `POLICY`。启用 RLS 后，如果当前角色受该策略约束，类似 `SELECT * FROM tenant_docs` 的查询只会返回当前角色或设置所允许看到的行。可以自由修改的 `app.tenant` 设置只是演示；真实应用必须阻止不受信任的客户端选择其他租户。

## 运行示例 {#run}

运行示例时，请使用一个不具备超级用户权限、没有 `BYPASSRLS` 属性，并且能够在练习数据库中创建表的角色。`FORCE ROW LEVEL SECURITY` 会让这张表的所有者也遵守策略。超级用户仍然可以看到两个租户。带注释的角色管理命令需要相应的角色管理权限。

```text
$ psql -f source/roles-and-grant.sql postgres
 current_user | session_user
--------------+--------------
 your_user    | your_user
(1 row)
 ...
```

## 常见陷阱 {#pitfalls}

- 忘记 `GRANT USAGE ON SCHEMA public`。角色可能已经对每张表都拥有 `SELECT`，但仍然无法查询，因为它缺少模式级的使用权限。
- 只为现有表授予权限，却没有配置 `DEFAULT PRIVILEGES`——应用创建的每张新表，读角色都无法访问。
- 将 `SECURITY DEFINER` 函数与宽松的 `search_path` 混用，会打开权限提升的通道。此类函数应始终设置 `search_path`。
- 在 `CREATE ROLE ... PASSWORD 'plain'` 中存储密码，对个人数据库来说可以接受；生产环境应使用 SCRAM 身份验证，绝不要在 SQL 脚本中硬编码密码。

> [!TIP] 提示
> 一个常见模式是使用三个角色：`app_owner`（拥有对象，用于迁移）、`app_read`（供分析和副本使用的只读角色）以及 `app_write`（供应用使用的读写角色）。让 `app_owner` 拥有对象，再向其他角色授予权限。

> [!NOTE] 动手练习
> 使用超级用户权限创建一个角色，只向它授予 `fruits` 上的 `SELECT`。然后以该角色重新连接（`psql -U newrole`），确认可以读取但不能写入。接着试验 RLS：在一张表上启用它，添加策略，并观察 `SET` 改变后 `SELECT` 的结果如何变化。

## 下载 {#downloads}

SQL 文件：[roles-and-grant.sql](/examples/source/roles-and-grant.sql) · [全部示例文件](/examples/examples.zip) · [准备工作](/#prerequisites)
