基于user模型
模式和用户同名
只有一个模式
# postgres用户连接
psql -U postgres -c "create user test password 'test';"
psql -U postgres -c "create database testdb with owner=test;"
psql -U postgres -d testdb -c "CREATE SCHEMA test AUTHORIZATION test;"
#test用户连接创建自定义对象:
psql -U test -d testdb -c "create table test_t(id int)"
这样的好处是,test用户连接testdb默认直接连接到了test模式,因为search_path默认值如下:
postgres=# select name,setting,unit,context from pg_settings where name ~* 'search_path';name | setting | unit | context
-------------+-----------------+------+---------search_path | "$user", public | | user
(1 row)
test用户为读写用户,后期如果需要使用只读用户,直接创建用户授权即可
-- 1、创建用户及指定密码:
create user readonly with encrypted password 'readonly';
-- 2、设置用户默认事务只读:
alter user readonly set default_transaction_read_only=on;
-- 3、赋予用户连接数据库权限:
grant connect on database testdb to readonly;
-- 4、切换到指定数据库:
\c testdb
-- 5、赋予用户权限,查看public模式下所有表:
grant usage on schema public to readonly;
alter default privileges in schema public grant select on tables to readonly;
-- 6、赋予指定模式下用户表、序列查看权限:
grant usage on schema test to readonly;
grant select on all sequences in schema test to readonly;
grant select on all tables in schema test to readonly;
alter default privileges in schema test grant select on tables to readonly;
有多个模式
创建数据库
psql -U postgres -c "create user test password 'test';"
psql -U postgres -c "create database testdb with owner=test;"
创建业务用户和模式
psql -U postgres -c "create user user1 password 'user1';"
psql -U postgres -c "create user user2 password 'user2';"
psql -U postgres -d testdb -c "CREATE SCHEMA user1 AUTHORIZATION user1;"
psql -U postgres -d testdb -c "CREATE SCHEMA user2 AUTHORIZATION user2;"
给业务用户授权数据库权限
psql -U postgres -c "grant all privileges on database testdb to user1;"
psql -U postgres -c "grant all privileges on database testdb to user2;"
需要几个模式就创建几个用户,用户和模式同名,用户只允许在和自己同名的模式下创建自定义对象,可以授权其他模式的读权限
授权user2查询user1模式下的查询权限:
grant usage on schema user1 to user2;
grant select on all tables in schema user1 to user2;
alter default privileges in schema user1 grant select on tables to user2;
后期如果还需要创建只读用户,和只有一个模式创建只读用户的场景一样
模式和用户不同名
-- 创建父角色(不能登录,昨晚对象的属主)
create role test;
-- 业务用户
create user test1 password 'test1';
create user test2 password 'test2';
grant test to test1,test2;
-- 创建数据库
create database testdb with owner=test;
-- 创建模式
\c testdb
create schema test_schema authorization test;
业务用户test1连接testdb库,创建自定义对象
-- psql -U test1 -d testdb
create table test_schema.test_t(id int);
注意此时test1用户创建的表test2用户无权访问(表的属主为test1)
可以把owner转为test用户即可
reassign owned by test1 to test;
基于role模型
权限管理设计模型
该模型简单有效,推荐绝大多数客户使用。
- 1个RDS PostgreSQL高权限账号,具有所有权限,由少量资深DBA掌握。
- 项目或者团队维度,1个资源owner账号,2个基本Role:
{project}_role_readwrite、{project}_role_readonly。 - 在Role的基础上创建业务账号:
业务账号user = role + login权限。 - 1个项目或团队可以有多个Schema。权限分配尽量以Schema或Role为单位。
- 业务表请勿放到
schema public中。因为PostgreSQL默认所有用户对schema public都有CREATE和USAGE权限。
权限规划示例
本示例以项目维度进行权限管理示例,同样也适用于团队维度。
- DBA拥有RDS PostgreSQL实例的高权限账号,名称是dbsuperuser。
- 业务项目名称是rdspg,新建schema名称是rdspg、rdspg_1。
项目中新增的资源owner账号和Role规划如下:
| user/Role | schema中表权限 | schema中存储过程权限 |
|---|---|---|
| rdspg_owner (user),是唯一的项目资源owner账号 | DDL:CREATE、DROP、ALTER DQL:SELECT DML:UPDATE、INSERT、DELETE |
DDL:CREATE、DROP、ALTER DQL:SELECT,调用存储过程 |
| rdspg_role_readwrite (role) | DQL:SELECT DML:UPDATE、INSERT、DELETE |
DQL(SELECT,调用存储过程) ,若存储过程有DDL操作,会抛出权限相关错误。 |
| rdspg_role_readonly (role) | DQL(SELECT) | DQL(SELECT,调用存储过程),若存储过程有DDL或者DML操作,会抛出权限相关错误。 |
新增业务账号时,根据不同需求,采用如下管理模式创建:
- rdspg_readwrite = rdspg_role_readwrite + login权限
- rdspg_readonly = rdspg_role_readonly + login权限
配置步骤:
1、创建项目资源owner账号rdspg_owner和项目Role。DBA使用dbsuperuser高权限账号执行如下操作。
-- rdspg_owner 是项目管理账号
CREATE USER rdspg_owner WITH LOGIN PASSWORD 'asdfy181BASDfadasdbfas';
-- 创建业务数据库
create database testdb owner=rdspg_owner;
\c testdbCREATE ROLE rdspg_role_readwrite;
CREATE ROLE rdspg_role_readonly;-- 设置: 对于rdspg_owner 创建的表,rdspg_role_readwrite 有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON TABLES TO rdspg_role_readwrite;-- 设置: 对于rdspg_owner 创建的SEQUENCES,rdspg_role_readwrite 有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON SEQUENCES TO rdspg_role_readwrite;-- 设置: 对于 rdspg_owner 创建的表, rdspg_role_readonly 只有 DQL(SELECT)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT SELECT ON TABLES TO rdspg_role_readonly;
2、创建rdspg_readwrite、rdspg_readonly业务账号。DBA使用dbsuperuser高权限账号执行如下操作。
-- rdspg_readwrite只有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
CREATE USER rdspg_readwrite WITH LOGIN PASSWORD 'dfandfnapSDhf23hbEfabf';
GRANT rdspg_role_readwrite TO rdspg_readwrite;-- rdspg_readonly只有 DQL(SELECT)权限。
CREATE USER rdspg_readonly WITH LOGIN PASSWORD 'F89h912badSHfadsd01zlk';
GRANT rdspg_role_readonly TO rdspg_readonly;
3、创建schema rdspg,并授权给项目Role。DBA使用dbsuperuser高权限账号执行如下操作。
-- schema rdspg的owner是 rdspg_owner账号
CREATE SCHEMA rdspg AUTHORIZATION rdspg_owner;-- 授权ROLE相关SCHEMA访问权限。
GRANT USAGE ON SCHEMA rdspg TO rdspg_role_readwrite;
GRANT USAGE ON SCHEMA rdspg TO rdspg_role_readonly;
**说明: **rdspg_readwrite和rdspg_readonly自动继承了相关Role的权限变更,不需要再额外操作。
应用场景示例
场景1:使用rdspg_owner账号:对schema rdspg中的表进行DDL(CREATE、DROP、ALTER)操作
\c testdb rdspg_owner
CREATE TABLE rdspg.test(id bigserial primary key, name text);
CREATE INDEX idx_test_name on rdspg.test(name);
场景2:使用 rdspg_readwrite/rdspg_readonly 账号进行业务开发
业务开发遵循最小权限原则,尽量使用rdspg_readonly账号,需要DML操作的地方才使用rdspg_readwrite账号。这样也方便在业务层做读写分离
说明:
- 业务层做读写分离,避免了自动读写分离中间件proxy带来的额外成本和性能损耗。
- 即使目前还没有使用只读实例,也建议区分 readonly客户端、readwrite客户端,为使用只读实例做准备。readonly客户端建议使用readonly账号,最小权限原则,规避权限误用。
- readonly客户端,使用readonly账号,设置JDBC URL:
只读实例1地址,只读实例2地址,读写实例地址。- readwrite客户端,使用readwrite账号,设置JDBC URL:
读写实例地址。
使用rdspg_readwrite账号,对schema rdspg中的表进行DQL(SELECT)、DML(UPDATE、INSERT、DELETE)操作:
\c testdb rdspg_readwrite
INSERT INTO rdspg.test (name) VALUES('name0'),('name1');
SELECT id,name FROM rdspg.test LIMIT 1;-- rdspg_readwrite没有 DDL(CREATE、DROP、ALTER)权限
CREATE TABLE rdspg.test2(id int);
ERROR: permission denied for schema rdspg
LINE 1: create table rdspg.test2(id int);DROP TABLE rdspg.test;
ERROR: must be owner of table testALTER TABLE rdspg.test ADD id2 int;
ERROR: must be owner of table testCREATE INDEX idx_test_name on rdspg.test(name);
ERROR: must be owner of table test
使用rdspg_readonly账号,对schema rdspg中的表进行DQL(SELECT)操作:
\c testdb rdspg_readonly
INSERT INTO rdspg.test (name) VALUES('name0'),('name1');
ERROR: permission denied for table testSELECT id,name FROM rdspg.test LIMIT 1;id | name
----+-------1 | name0
(1 row)
场景3:不同项目交叉授权
有个新项目employee,创建role和user以及授权如下:
\c testdb dbsuperuser
CREATE ROLE employee_role_readwrite;
CREATE ROLE employee_role_readonly;ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON TABLES TO employee_role_readwrite;
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON SEQUENCES TO employee_role_readwrite;
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT SELECT ON TABLES TO employee_role_readonly;CREATE USER employee_readwrite WITH LOGIN PASSWORD 'dfandfnapSDhf23hbEfabf';
GRANT employee_role_readwrite TO employee_readwrite;
CREATE USER employee_readonly WITH LOGIN PASSWORD 'F89h912badSHfadsd01zlk';
GRANT employee_role_readonly TO employee_readonly;
现在需要为账号employee_readwrite增加rdspg项目的表只读权限。DBA使用dbsuperuser高权限账号做如下操作:
-- 给账号 employee_readwrite 加上 rdspg_role_readonly 权限集合。
\c testdb dbsuperuser
GRANT rdspg_role_readonly TO employee_readwrite;
场景4:项目新增 schema rdspg_1,并授权给项目Role
rdspg_readwrite、rdspg_readonly、employee_readwrite账号自动继承了相关Role的权限变更,不需要再额外操作。DBA使用dbsuperuser 高权限账号做如下操作:
\c testdb dbsuperuser
CREATE SCHEMA rdspg_1 AUTHORIZATION rdspg_owner;-- 授权ROLE相关SCHEMA访问权限。
-- CREATE 使得 rdspg_owner 对schema rdspg_1中的表有 DDL(CREATE、DROP、ALTER)权限。
GRANT USAGE ON SCHEMA rdspg_1 TO rdspg_role_readwrite;
GRANT USAGE ON SCHEMA rdspg_1 TO rdspg_role_readonly;
账号权限查询
psql客户端,使用元命令\du查看所有用户和拥有的角色:

从上述查询结果示例中可以看出:employee_readwrite账号的Member of列中,内容为rdspg_role_readonly,employee_role_readwrite,因此,此账号对employee项目表具有DQL和DML权限,对rdspg项目表具有DQL权限。
使用SQL查询
SELECT r.rolname, r.rolsuper, r.rolinherit,r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,r.rolconnlimit, r.rolvaliduntil,ARRAY(SELECT b.rolnameFROM pg_catalog.pg_auth_members mJOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)WHERE m.member = r.oid) as memberof
, r.rolreplication
, r.rolbypassrls
FROM pg_catalog.pg_roles r
WHERE r.rolname !~ '^pg_'
ORDER BY 1;
参考资料
https://help.aliyun.com/zh/rds/apsaradb-rds-for-postgresql/manage-permissions-in-an-apsaradb-rds-for-postgesql-instance
https://support.huaweicloud.com/bestpractice-rds-pg/rds_pg_0035.html