你有没有遇到过这种情况:GBase 8s里辛辛苦苦建好一个账号,该授的权限也授了,用户登录后却什么都查不了。问了一圈,发现是会话里缺了一条SET ROLE。这种情况在缺省角色概念出现之前太常见了,尤其当权限是通过角色下发的时候。后来我意识到问题不在用户记性差,而在权限设计本身——你把权限放进了角色里,却没有提供一个“让角色自动生效”的机制。GBase 8s里的缺省角色(DEFAULT ROLE),就是专门来解决这件事的。
这篇文章不打算讲太虚的东西,就围绕缺省角色说清楚几件事:它解决什么痛点、背后的角色权限机制长什么样、怎么一步步定义、生效边界在哪里、实操中哪些坑我替你踩过。如果你正在管理GBase 8s的权限,或者刚接手一个角色权限混乱的库,这篇应该值得你花十分钟看完。
1. 缺省角色到底解决了什么问题:三个让人头疼的权限场景
1.1 会话级角色激活:用户永远记不住SET ROLE
在GBase 8s里,角色不会天然生效。你要先GRANT一个角色给用户,用户登录之后还要执行SET ROLE,角色里的权限才会真正落到会话上。问题就出在这个“还要执行”上。
正常开发人员谁会记得?他们只知道账号密码,登录后直接SELECT。结果就是各种“为什么查不了”“昨天还可以今天怎么不行了”的工单。更麻烦的是跑批任务,凌晨的JOB如果没在脚本里写SET ROLE,报错日志里只会留下一句权限不足,排查起来得翻半天。
缺省角色解决的就是这个“默认生效”问题。你把它定义好,用户登录的那一刻角色就自动激活了,不需要用户在客户端做任何额外动作。一套配置,所有派生出来的会话都受用。这个特性对降低日常权限工单量的帮助是立竿见影的。
1.2 直接授权一时爽,权限回收火葬场
有一种偷懒做法很常见:反正用户就几个人,直接把表权限授给用户算了。我刚管库那会儿也这么干过。用户少还好,用户一多,授权关系就像蜘蛛网。更要命的是,有人离职之后,他的账号可能还挂着十几个表的权限,没人敢删,怕影响业务。
角色机制能让这件事变得清爽。权限集中放在角色上,用户只是角色的成员。离职了,一条REVOKE把角色摘掉,权限干干净净跟着走。而缺省角色在其中的作用,是让这个“干净”的标准贯彻到登录环节——不用每次靠人肉提醒去SET ROLE,权限的基准状态从一开始就是你设计好的那一套。
1.3 多角色切换的日常繁琐
有些账号不是“一个人”在用。比如一个应用账号,白天给报表查询用途,晚上要跑数据修正的批量任务。查询只需要只读角色,批量任务需要读写角色。不肯切角色吧,权限太大反而容易出事;老老实实切吧,每次都得先SET ROLE再干活。
如果把这个账号的缺省角色设成使用频率最高的只读角色,大部分时候登录即用。需要批量任务权限时再临时切到另一个角色,用完切回来。日常操作省了一堆事,权限结构也清晰多了。这个场景很多团队都遇到过,只是没意识到缺省角色能顺手解决掉。
2. 搞懂缺省角色之前,先搞懂角色是怎么玩的
2.1 一句话理解角色:权限的集装箱
角色是什么?我习惯把它理解成“权限的集装箱”。你不需要一次一次往用户身上贴表权限、过程权限,而是先把这些权限打包放进一个角色,再把角色分配给用户。
用生活里的事打比方:一张门禁卡,卡里面可以挂多个门禁权限组。你去哪间会议室,取决于卡上被勾选了哪个权限组。用户-角色-权限三者就是这么个关系。缺省角色相当于门禁系统里默认勾选的那一组——你刷卡进门,自动就是这个组的权限范围。
2.2 角色相关的核心语句
先梳理一下和角色、缺省角色相关的SQL,方便后面串起来看。这张表建议存一下,后面实操频繁会用到。
| 操作 | SQL | 说明 |
|---|---|---|
| 创建角色 | CREATE ROLE r_readonly | 定义一个新的角色容器 |
| 给角色授对象权限 | GRANT SELECT ON tab TO r_readonly | 把权限装进角色 |
| 把角色授予用户 | GRANT r_readonly TO zhangsan | 让用户拥有这个角色 |
| 设置缺省角色 | ALTER USER zhangsan WITH DEFAULT ROLE r_readonly | 指定登录自动激活的角色 |
| 会话内切换角色 | SET ROLE r_readonly | 手动激活某个角色 |
| 恢复缺省角色 | SET ROLE DEFAULT | 回到默认状态 |
注意GRANT在这张表里出现两次。一次是“授权给角色”,一次是“把角色给人”,两者作用是不同层面的。很多新手在这里犯迷糊:GRANT r_readonly TO zhangsan这条语句发给库之后,以为权限直接生效了,结果用户一查表还是被拒绝——因为角色是给了,但会话里的角色还没有激活。这个和2.3连起来看就明白了。
2.3 登录瞬间发生了什么
假设用户zhangsan拥有角色r_readonly,并且这个角色被设成了缺省角色。当他用客户端登录GBase 8s时,数据库在建立会话的过程中会做一件事:把DEFAULT ROLE指定的那个角色自动激活到这个会话上。
效果等同于数据库替你执行了一句SET ROLE r_readonly。用户从头到尾不需要知道自己有什么角色,也不需要做任何操作,登录之后SELECT权限就在了。这就是“缺省角色”最核心的机制。
反过来,如果用户拥有角色但缺省角色没有配置,登录后这个角色是“休眠”的。除非手动执行SET ROLE,否则角色里的权限一个都不会生效。这也是很多权限“看起来配了但用不了”的根本原因。
3. 手把手定义缺省角色:从零到一的全过程
3.1 动手之前先确认版本支持
DEFAULT ROLE不是一个很古老的功能属性。你在动手前,先确认一下当前实例版本。可以用onstat工具或者管理客户端查看版本号,也可以直接翻一下随版本发布的SQL指南。就我的经验,较新的8.x系列版本都支持,但具体哪个版本开始引入、语法细节是否略有调整,必须以你手上这套环境的官方手册为准。
另外,执行这些操作的人需要有对应的权限。通常由DBA操作,或者至少具备CREATE ROLE、GRANT、ALTER USER等语句的执行权限。建议先在测试实例上把流程走一遍,确认无误再上生产。
3.2 创建角色并装载权限
先建一个角色。以只读分析角色为例:
CREATE ROLE r_readonly;然后往角色里装权限。给指定表授权:
GRANT SELECT ON TABLE t_orders TO r_readonly; GRANT SELECT ON TABLE t_customers TO r_readonly; GRANT SELECT ON TABLE t_products TO r_readonly;如果某个存储过程也需要允许执行,一并挂到角色上:
GRANT EXECUTE ON PROCEDURE sp_daily_report TO r_readonly;这里的原则是:权限尽量收进角色,不要散落在用户级别。后面管理时,只维护角色这一个入口就够了。如果需要调整某类人的访问范围,改角色的权限集即可,所有拥有这个角色的用户会同步受到影响。
3.3 把角色授予用户,并设置成缺省角色
角色装好权限之后,先把它授予目标用户:
GRANT r_readonly TO zhangsan;然后用ALTER USER把该角色指定为缺省角色:
ALTER USER zhangsan WITH DEFAULT ROLE r_readonly;如果是新建用户,可以一步到位:
CREATE USER 'zhangsan' IDENTIFIED BY 'P@ssw0rd' WITH DEFAULT ROLE 'r_readonly';顺序很重要:先让用户拥有角色,再设置缺省角色。如果角色还没有授予用户,就去设置缺省角色,数据库会直接报错或者拒绝处理。这点在第5章会单独展开讲。
记住这个顺序:先GRANT角色给用户,再ALTER USER设置缺省角色。反过来大概率报错。
3.4 验证缺省角色是否真的生效
配置完成之后别急着交差,做一次黑盒验证最靠谱。
新开一个会话,不执行任何SET ROLE语句,直接查r_readonly角色权限下的那张表:
SELECT * FROM t_orders LIMIT 1;能查出数据,说明登录时缺省角色自动激活了。如果报权限不足,就按下面的链路排查:
- 确认角色确实已GRANT给该用户;
- 确认ALTER USER语句执行成功,无报错;
- 确认你用的连接不是复用了一个很久之前建立的旧会话;
- 在新会话中执行SELECT测试,而不是在老会话里测。
4. 缺省角色不是“唯一角色”:生效边界与权限叠加的规则
4.1 一个用户可以有多个角色,但缺省只有一个
用户和角色是多对多关系。一个用户可以拥有r_readonly和r_write等多个角色,但DEFAULT ROLE只能设定一个。也就是说,登录时自动激活的只有那一个角色,其他角色处于休眠,需要手动SET ROLE切换才生效。
这里要提醒一句:在我接触过的GBase 8s版本里,会话内同时激活的角色只有一个,语义上是以最后一次SET ROLE为准。这一点和常见的关系型数据库产品不太一样,设计权限时别默认“拥有即生效”,一定区分“拥有”和“当前激活”两个状态。
实际场景中,用户卡在“我明明有这个角色,为什么权限不对”的时候,十有八九是当前激活的角色不是他以为的那个。用SET ROLE切一下,或者直接用SET ROLE DEFAULT回到缺省角色,问题通常就解决了。
4.2 直接授权与角色授权:权限取并集
有一个细节容易被忽略:用户直接收到的授权,和通过当前激活角色拿到的授权,是并集关系。
| 场景 | 当前激活角色权限 | 用户直接权限 | 最终生效权限 |
|---|---|---|---|
| 操作A | 允许 | 拒绝 | 允许 |
| 操作B | 拒绝 | 允许 | 允许 |
| 操作C | 拒绝 | 拒绝 | 拒绝 |
也就是说,哪怕你把某个权限从角色里REVOKE掉,只要该用户之前被直接授过这个权限,他照样能访问。反过来也一样,直接REVOKE用户的权限,只要角色里还留着,通过角色他依然能访问。
所以做权限收敛的时候,不能只改角色。你得同时检查“用户直接被授的权限”和“用户通过角色拿到的权限”两本账,才算彻底。这是我踩过不少坑之后得出的结论,尤其是接手老库的时候,里面往往积压了大量历史直接授权。
4.3 WITH GRANT OPTION:默认角色可能带来的权限扩散
如果授权给角色时带了WITH GRANT OPTION,那么拥有这个角色(且角色处于激活状态)的用户,可以把自己手上的权限再转授给其他人。
举个例子:角色r_query对某张核心表有SELECT权限,并且是WITH GRANT OPTION授进来的。业务人员把角色设成缺省角色后,他登录就有转授权限的能力。如果他没有安全意识,顺手把权限转给了别人,后续审计时你会发现授权链条变得很难追溯。
我的建议:核心、敏感对象的授权,尤其涉及WITH GRANT OPTION时,尽量不下放到角色,更不要让带这种标记的角色成为缺省角色。缺省角色是高频默认态,默认态里挂着转授权,风险面会放大很多。
5. 实战排查:定义缺省角色时最容易踩的四个坑
5.1 角色还没授予用户,就急着设置缺省角色
现象:执行ALTER USER zhangsan WITH DEFAULT ROLE r_readonly时,数据库提示类似“用户与角色之间的成员关系不存在”或者直接报错。这种情况最常见的原因就是GRANT r_readonly TO zhangsan根本没有执行,或执行失败没注意。
正确做法是严格按步骤来:先GRANT角色,再设置缺省角色。如果你在管理多个环境,还要小心确认是在同一个实例上执行的。跨实例的授权和设置缺省角色,根本就是两回事,别这边设置完那边发现用户压根不在这个库上。
5.2 改完缺省角色不生效:先有会话过期,还有手动角色覆盖
这是生产环境里最像“灵异事件”的一个坑。DBA明明把缺省角色从r_readonly改成了r_write,用户却反馈权限一点变化没有。
第一个原因:缺省角色是在会话建立时生效的。用户手里已经打开的连接,仍然保留着旧会话的激活状态,不会因为你改了设置就自动跟着变。如果是应用通过连接池复用老连接,那就更典型了——连接池里每个连接的会话状态都可能是“历史遗留”。处理办法是让应用重连,或者重启连接池,让新会话去读取最新配置。
第二个原因:用户登录之后自己手动SET ROLE到别的角色了。这种覆盖是合法的,数据库不会拦,但你查半天查不到问题。验证方法很简单,让用户执行SET ROLE DEFAULT,再看权限状态是否恢复成你配置的预期。
排查这类问题,我的经验是先把“会话是否新建”和“当前激活的角色是谁”这两件事确认掉,再去看配置有没有写对。顺序反了容易白忙活。
实际排查顺序建议:先确认会话是否新建,再确认当前激活的角色,最后才去复查配置。
5.3 DROP ROLE之后的连锁反应
有时候因为业务调整,要把整个角色删掉。如果直接DROP ROLE,可能出现两种情况:一是角色仍有成员,数据库拒绝删除;二是强删之后,用户这边还残留着对缺省角色的引用,登录时出现异常。
我的建议是删除角色前先做一次清理:查清楚哪些用户拥有这个角色,逐一REVOKE;然后把用户里指向该角色的DEFAULT ROLE配置改掉或清掉;最后再执行DROP ROLE。顺序做对,删除才干净,后续不会有用户登录时被“引用了一个不存在的角色”这种怪问题。
5.4 导入导出脚本把缺省角色弄丢
现在很多人会在不同环境之间同步用户和授权数据,可能是GBase 8s自带的模式导出工具,也可能是第三方同步脚本。这类工具导出对象权限、角色成员一般都能导出来,但“缺省角色”这种偏配置性的属性,往往是最容易在迁移过程中丢掉的。
我遇到过的情况是:测试库验证一切正常,导出到生产库之后,用户登录查表各种报错。查了一圈,角色和GRANT都在,就是DEFAULT ROLE没有跟着导过去。用户登录后角色根本没激活,权限自然就是空的。
处理办法:不要在导入导出之后想当然认为“结构和权限一致”。每迁移一批用户,单独核对一次DEFAULT ROLE设置,必要时用ALTER USER补一遍。这个动作很便宜,能帮你省掉后续大量排查工单。
6. 让缺省角色成为权限治理的锚点:几个长期好用的习惯
6.1 按岗位建模,而不是按人建模
用得久了你会发现,缺省角色最大的价值不是省事,而是逼着你把权限设计变成“建角色-挂权限-配用户”的标准流程。我给团队定的规矩是:先有角色,后有用户;用户默认都配一个缺省角色。
角色按岗位或应用模块建模,比如:
- r_readonly:只读分析;
- r_business_write:业务录入与修改;
- r_batch:批量处理;
- r_dba:管理维护。
新员工入职,创建用户、挂上对应角色、设置缺省角色,三步走完。转岗或者离职,REVOKE角色即可。不需要再经历“这个人到底有哪些表的权限”的考古式排查。
6.2 审计时先看默认角色,再看额外角色
做权限审计时,我会先拉一份“用户-缺省角色”清单。缺省角色决定了每个用户登录后的基准权限,这一层必须干净。然后再看用户拥有的其他角色和直接授权,确认是否存在过度授权。
比较实用的检查项:
- 是否所有用户都设置了缺省角色;
- 是否存在长期不登录但拥有大量角色的账号;
- 是否有角色带WITH GRANT OPTION授给了过多用户;
- 是否有敏感表的权限直接授到了用户级别、绕过了角色。
这些检查不一定需要复杂的脚本,几条查询就能跑完。关键是坚持定期做,而不是等到安全事件发生了才去翻。
6.3 我坚持下来的三个操作习惯
第一,所有权限变更脚本都记录下来,走版本管理,不手工在命令行里敲。这样出了问题可以回看历史,知道哪个时间点改了什么。
第二,每次变更先在测试实例上完整验证,包括“新会话登录后权限是否符合预期”这一步。生产环境直接操作,永远是最后手段。
第三,和业务方约定一个窗口做权限变更,错开批量任务和报表高峰期。不然你正改着角色,凌晨的任务报错,锅还是你的。
最后聊聊我的整体感受。把缺省角色用起来之后,权限相关的工单数量明显降了一个台阶。最直接的变化是:用户不再因为“忘记SET ROLE”这种问题来烦我,权限的基准状态也从“靠人自觉”变成了“系统自动”。
如果你目前的环境还是直接授权加手动切角色的模式,建议抽个时间,把高频账号梳理一遍,先建角色、再挂权限、最后设缺省角色。改动过程中记住一个原则:先小范围验证,再推广到全库。另外再提醒一句,不同版本的GBase 8s对DEFAULT ROLE的支持细节可能存在差异,动手之前一定先翻你的版本手册。