基础脚本

-- 创建用户
CREATE USER readonly WITH PASSWORD 'TJJe5CvU';

-- 授予连接数据库权限
GRANT CONNECT ON DATABASE jgsteel_prm TO readonly;

-- 授予 schema 使用权限
GRANT USAGE ON SCHEMA public TO readonly;

-- 授予现有表查询权限
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

-- 设置默认权限:未来新建的表也自动授予 SELECT
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;

这个脚本能跑通,但有几个可以改进的地方。


生产环境强化版

-- ============================================
-- Create Read-Only User for PostgreSQL
-- ============================================

-- Step 1: Create login role (idempotent)
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'readonly') THEN
        CREATE ROLE readonly WITH LOGIN PASSWORD 'TJJe5CvU';
        RAISE NOTICE 'Role [readonly] created.';
    ELSE
        RAISE NOTICE 'Role [readonly] already exists.';
    END IF;
END
$$;

-- Step 2: Grant CONNECT on target database
GRANT CONNECT ON DATABASE jgsteel_prm TO readonly;

-- Step 3: Switch to target database (run these inside the database)
-- \c jgsteel_prm

-- Step 4: Grant schema usage
GRANT USAGE ON SCHEMA public TO readonly;

-- Step 5: Grant SELECT on ALL existing tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

-- Step 6: Set default privileges for future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;

-- Step 7: (Optional) Grant SELECT on sequences if needed
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO readonly;

-- Step 8: (Optional) Revoke write permissions explicitly
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM readonly;

关键点解读

1. ALTER DEFAULT PRIVILEGES — 最容易遗漏的一步

GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

这条命令只对执行时已存在的表生效。未来新建的表不会自动获得权限。

ALTER DEFAULT PRIVILEGES 解决了这个问题 — 之后在 public schema 中新建的任何表,都会自动授予 readonly 用户 SELECT 权限。

2. 权限层级关系

PostgreSQL 的权限是分层级的,缺一不可

CONNECT on DATABASE   →  允许连接数据库
      ↓
USAGE on SCHEMA       →  允许查看 schema 中的对象
      ↓
SELECT on TABLES      →  允许查询表数据

3. 为什么不需要 DENY

PostgreSQL 没有像 SQL Server 那样的 DENY 语句。权限默认就是"最小权限原则" — 只授予你明确需要的权限,未授予的操作默认拒绝。


验证权限

-- 查看用户拥有的角色和属性
\du readonly

-- 查看具体权限
SELECT 
    grantee,
    table_schema,
    table_name,
    privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'readonly';

-- 测试连接和查询
-- psql -U readonly -d jgsteel_prm -c "SELECT current_user, now();"
-- psql -U readonly -d jgsteel_prm -c "SELECT * FROM your_table LIMIT 5;"
-- psql -U readonly -d jgsteel_prm -c "DELETE FROM your_table;"  -- 应该报错

连接字符串示例

# JDBC
jdbc:postgresql://192.168.1.100:5432/jgsteel_prm?user=readonly&password=TJJe5CvU

# psql CLI
psql -h 192.168.1.100 -p 5432 -U readonly -d jgsteel_prm

# pgAdmin / DBeaver
Host: 192.168.1.100
Port: 5432
Database: jgsteel_prm
Username: readonly
Password: TJJe5CvU

与 SQL Server 的对比

操作 SQL Server PostgreSQL
创建登录 CREATE LOGIN CREATE ROLE ... WITH LOGIN
创建用户 CREATE USER ... FOR LOGIN 角色即用户(无需额外创建)
Schema 权限 GRANT SELECT ON SCHEMA::dbo GRANT USAGE ON SCHEMA public + GRANT SELECT ON ALL TABLES
默认权限 无原生支持 ALTER DEFAULT PRIVILEGES
显式拒绝 DENY INSERT 不支持(最小权限原则)

Professional English Terms

Term Meaning
ALTER DEFAULT PRIVILEGES 设置默认权限,确保未来创建的对象自动继承权限
Schema USAGE Schema 使用权限,允许查看和访问 schema 中的对象
Role / Login Role PostgreSQL 中的角色概念,CREATE ROLE ... WITH LOGIN 等价于创建用户
Idempotent 幂等性,用 DO $$ ... IF NOT EXISTS 确保脚本可重复执行
pg_hba.conf PostgreSQL 客户端认证配置文件,控制允许哪些 IP 连接