一、为什么表权限还不够

普通权限控制的是:

1
某个用户能不能访问某张表

例如:

1
GRANT SELECT ON sales.orders TO report_user;

这表示 report_user 可以查询 sales.orders

但很多企业场景需要更细的控制:

1
2
3
4
5
销售只能看自己区域的订单
医生只能看本科室病人的记录
租户只能访问自己的数据
分公司只能看本公司的财务数据
客服只能看被分配给自己的工单

这些不是“能不能访问表”的问题,而是:

1
能访问表里的哪些行

这就需要 PostgreSQL 的 RLS。

二、RLS 是什么

RLS 是 Row Level Security,行级安全。

表级权限控制:

1
能不能 SELECT orders 表

RLS 控制:

1
SELECT orders 时能看到哪些行

例如 orders 表中有字段:

1
2
3
tenant_id
department_id
owner_user

RLS 可以根据当前会话、当前用户或应用设置的上下文,只返回符合条件的行。

三、RLS 基本示例

示例表:

1
2
3
4
5
6
CREATE TABLE sales.orders (
    id bigint PRIMARY KEY,
    tenant_id text NOT NULL,
    order_no text NOT NULL,
    amount numeric(12,2) NOT NULL
);

启用 RLS:

1
ALTER TABLE sales.orders ENABLE ROW LEVEL SECURITY;

创建策略:

1
2
3
4
5
6
CREATE POLICY tenant_select_policy
ON sales.orders
FOR SELECT
USING (
    tenant_id = current_setting('app.current_tenant')
);

这表示查询 sales.orders 时,只能看到:

1
tenant_id = 当前会话 app.current_tenant

应用连接数据库后设置:

1
SET app.current_tenant = 'tenant_001';

再查询:

1
SELECT * FROM sales.orders;

只能看到 tenant_001 的数据。

四、RLS 的 USING 和 WITH CHECK

RLS 策略中常见两个条件:

1
2
USING:控制哪些已有行可见或可操作
WITH CHECK:控制新写入或更新后的行是否允许存在

查询策略:

1
2
3
4
CREATE POLICY tenant_select_policy
ON sales.orders
FOR SELECT
USING (tenant_id = current_setting('app.current_tenant'));

插入策略:

1
2
3
4
CREATE POLICY tenant_insert_policy
ON sales.orders
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.current_tenant'));

更新策略:

1
2
3
4
5
CREATE POLICY tenant_update_policy
ON sales.orders
FOR UPDATE
USING (tenant_id = current_setting('app.current_tenant'))
WITH CHECK (tenant_id = current_setting('app.current_tenant'));

含义:

1
2
USING:只能更新自己租户原本可见的行
WITH CHECK:更新后 tenant_id 仍然必须是自己的租户

否则用户可能把自己的行改成别的租户,或插入不属于自己的数据。

五、RLS 和普通权限的关系

RLS 不是替代表权限,而是叠加在表权限之上。

用户必须先有表权限:

1
GRANT SELECT ON sales.orders TO app_user;

然后 RLS 再决定:

1
在可查询这张表的前提下,能看到哪些行。

如果没有表权限,RLS 不会让用户凭空获得访问能力。

因此访问控制顺序可以理解为:

1
2
3
4
1. 是否能连接数据库
2. 是否能使用 schema
3. 是否有表权限
4. 是否通过 RLS 策略

六、RLS 注意事项

RLS 很强,但使用时要注意:

1
2
3
4
5
1. 超级用户通常可以绕过 RLS
2. 表 owner 默认可能绕过 RLS,必要时使用 FORCE ROW LEVEL SECURITY
3. 策略条件要简单、稳定、可理解
4. 应用必须可靠设置会话上下文
5. 要避免连接池复用导致上下文串租户

强制 owner 也遵守 RLS:

1
ALTER TABLE sales.orders FORCE ROW LEVEL SECURITY;

查看策略:

1
2
3
4
SELECT *
FROM pg_policies
WHERE schemaname = 'sales'
  AND tablename = 'orders';

关闭 RLS:

1
ALTER TABLE sales.orders DISABLE ROW LEVEL SECURITY;

删除策略:

1
DROP POLICY tenant_select_policy ON sales.orders;

如果使用连接池,必须确保每次请求都正确设置并清理上下文,例如:

1
SET app.current_tenant = 'tenant_001';

或者在事务内使用:

1
SET LOCAL app.current_tenant = 'tenant_001';

SET LOCAL 只在当前事务内有效,更适合连接池场景。

七、审计为什么重要

权限设计解决的是:

1
谁应该能做什么

审计解决的是:

1
谁实际做了什么

企业环境中,以下操作通常需要审计:

  • 创建、修改、删除表
  • 创建、修改、删除用户
  • 授权和回收权限
  • 大批量更新和删除
  • 访问敏感表
  • 执行高危函数
  • 登录失败
  • 异常来源 IP 连接

没有审计,出问题后很难定位责任和影响范围。

八、pgAudit 基本思路

PostgreSQL 常用审计扩展是 pgaudit

通常需要在配置中加载:

1
ALTER SYSTEM SET shared_preload_libraries = 'pgaudit';

然后重启 PostgreSQL。

创建扩展:

1
CREATE EXTENSION pgaudit;

配置审计类型:

1
2
ALTER SYSTEM SET pgaudit.log = 'ddl, write, role';
ALTER SYSTEM SET pgaudit.log_relation = on;

重新加载配置:

1
SELECT pg_reload_conf();

常见审计类别:

1
2
3
4
5
6
read:读操作
write:写操作
function:函数调用
role:角色和权限相关操作
ddl:DDL 操作
misc:其他杂项

实际生产中不要盲目打开所有审计,否则日志量可能非常大。

更推荐按系统重要性、合规要求和日志容量进行配置。

九、查看连接与活跃会话

查看当前连接:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
SELECT
    pid,
    usename,
    datname,
    client_addr,
    state,
    application_name,
    backend_start,
    query_start,
    wait_event_type,
    wait_event,
    query
FROM pg_stat_activity
ORDER BY query_start NULLS LAST;

排查异常连接时重点看:

1
2
3
4
5
6
usename:用户
client_addr:客户端地址
application_name:应用名
state:连接状态
query_start:SQL 开始时间
query:正在执行的 SQL

可以要求应用配置明确的 application_name,方便定位来源。

十、密码安全

建议使用 SCRAM-SHA-256:

1
2
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();

之后新设置的密码会使用更安全的加密方式:

1
ALTER ROLE app_user PASSWORD 'NewStrongPassword';

创建用户时设置有效期:

1
2
3
CREATE USER report_user
WITH PASSWORD 'StrongPassword'
VALID UNTIL '2026-12-31';

临时禁用账号:

1
ALTER ROLE report_user NOLOGIN;

恢复登录:

1
ALTER ROLE report_user LOGIN;

十一、pg_hba.conf 认证控制

PostgreSQL 的连接认证主要由 pg_hba.conf 控制。

典型格式:

1
host    database    user    address        auth-method

示例:

1
host    sales_db    app_user    10.0.0.0/8    scram-sha-256

表示:

1
允许 10.0.0.0/8 网段的 app_user 连接 sales_db,使用 scram-sha-256 认证。

更严格的 SSL 示例:

1
hostssl    all    all    10.0.0.0/8    scram-sha-256

表示只允许 SSL 连接。

修改 pg_hba.conf 后需要 reload:

1
SELECT pg_reload_conf();

或者使用系统命令 reload PostgreSQL 服务。

十二、证书认证

安全要求更高的场景,可以使用客户端证书认证。

示例:

1
hostssl all all 10.0.0.0/8 cert clientcert=verify-ca

含义:

1
2
3
hostssl:必须使用 SSL
cert:使用客户端证书认证
clientcert=verify-ca:验证客户端证书由可信 CA 签发

证书认证适合:

  • 企业内网服务间访问
  • 金融、医疗等高合规场景
  • 不希望只依赖密码的系统
  • 固定服务身份认证

实际落地时需要配合服务端证书、客户端证书、CA、证书吊销和更新流程。

十三、权限问题排查清单

当遇到:

1
permission denied

可以按下面顺序排查。

确认角色:

1
2
3
SELECT rolname, rolcanlogin, rolinherit
FROM pg_roles
WHERE rolname = 'app_user';

确认数据库连接权限:

1
SELECT has_database_privilege('app_user', 'sales_db', 'CONNECT');

确认 schema 权限:

1
2
SELECT has_schema_privilege('app_user', 'sales', 'USAGE');
SELECT has_schema_privilege('app_user', 'sales', 'CREATE');

确认表权限:

1
2
3
4
SELECT has_table_privilege('app_user', 'sales.orders', 'SELECT');
SELECT has_table_privilege('app_user', 'sales.orders', 'INSERT');
SELECT has_table_privilege('app_user', 'sales.orders', 'UPDATE');
SELECT has_table_privilege('app_user', 'sales.orders', 'DELETE');

确认序列权限:

1
SELECT has_sequence_privilege('app_user', 'sales.orders_id_seq', 'USAGE');

确认 RLS:

1
2
3
4
SELECT *
FROM pg_policies
WHERE schemaname = 'sales'
  AND tablename = 'orders';

确认当前会话上下文:

1
2
3
SHOW search_path;
SELECT current_user;
SELECT session_user;

如果使用自定义上下文:

1
SELECT current_setting('app.current_tenant', true);

十四、安全基线建议

生产环境建议至少做到:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
1. 应用不使用超级用户
2. 回收 public schema 对 PUBLIC 的 CREATE 权限
3. 按 schema 和角色组授权
4. 只读、读写、DDL、备份、监控账号分离
5. 高危权限定期审计
6. 使用 scram-sha-256
7. 合理配置 pg_hba.conf 来源网段
8. 重要系统启用审计
9. 多租户或敏感数据场景考虑 RLS
10. 连接池场景谨慎处理 SET/SET LOCAL 上下文

常用安全初始化:

1
2
3
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();

十五、总结

PostgreSQL 的权限体系不仅包含普通的 GRANTREVOKE,还可以进一步扩展到:

1
2
3
4
5
6
RLS:控制行级数据可见性
pgAudit:记录关键操作
pg_hba.conf:控制连接来源和认证方式
密码策略:提高账号安全性
证书认证:增强服务身份可信度
权限审计:发现异常授权和高危账号

企业实践中,建议先建立清晰的角色和 schema 权限体系,再根据业务敏感度补充 RLS、审计和连接安全。

权限管理的最终目标不是“权限越多越方便”,而是:

1
2
3
4
该访问的人能访问
不该访问的人访问不了
做过什么操作能追溯
账号泄露或误操作时影响范围可控