Redis 6.0 ACL权限控制实战:从粗放密码到精细化安全隔离
2026/9/17 14:14:08 网站建设 项目流程

1. 先搞清楚一件事:Redis 6.0 的 ACL 到底解决了什么

用过 Redis 的人都知道,在 6.0 之前,Redis 的权限控制其实约等于没有。你打开redis.conf,能找到的安全相关配置无非就是一个requirepass,设置一个全局密码。所有连上来的客户端,只要拿着这同一个密码,就能执行 Redis 里所有的命令——FLUSHALLCONFIG SETSHUTDOWNKEYS *,想干嘛就干嘛。这在生产环境里是非常吓人的一件事情。想象一下,你给业务团队开了一个 Redis 实例,结果因为某个人误操作执行了FLUSHALL,整个缓存瞬间清空,这口锅谁背都背不动。

Redis 6.0 推出的 ACL(Access Control List,访问控制列表)就是来解决这个问题的。它允许你针对不同的用户、不同的连接,去精细地控制“能执行哪些命令”“能访问哪些 key”,甚至还能限制用户连接的客户端地址。说白了,就是从以前“一把钥匙开一把锁”的粗放模式,进化成了“一人一卡,刷哪儿管哪儿”的精细化权限管理模式。

我个人的观点是,ACL 的引入是 Redis 从“缓存工具”走向“数据基础设施”的关键一步。在 6.0 之前,很多公司根本不敢把 Redis 直接暴露给多个业务方共用,因为没法隔离风险。有了 ACL,你完全可以开一个实例,给订单服务、用户服务、消息队列各分配一个独立的账号,各自只能访问自己的 key 前缀,只能执行自己业务需要的命令。这个变化对于架构层面的安全隔离来说,意义是非常大的。

这篇文章,我就结合自己的实际使用经验,把 ACL 从概念、命令、配置到实战踩坑,完整地过一遍。内容偏实操,看完你直接能在自己的环境里配一套像样的权限体系出来。

2. ACL 的核心概念拆解:用户、规则、权限,别搞混了

ACL 这个东西听起来高大上,但它的模型其实非常简单,你可以把它类比成公司门禁系统。

首先,Redis 里会有多个用户(User)。默认有一个超级管理员用户叫default,默认情况下他是最高权限,什么都能干,谁连接 Redis 如果没有指定用户名,用的就是这个default用户。

其次,每个用户底下会挂一堆规则(Rule)。这些规则定义了这个人能做什么、不能做什么。比如,规则可以规定该用户能不能执行CONFIG命令,能不能访问order:*这组 key,密码是什么,等等。

最后,当客户端连接 Redis 的时候,需要提供用户名 + 密码来认证。认证通过后,这个连接的所有操作都会受到该用户规则的限制。一旦越权,Redis 会直接返回错误。

让我用一句好记的话来总结:用户是身份的载体,规则是权限的描述,命令和 key 是被控制的对象。

这里有个地方要特别注意:在 Redis 6.0 中,即使你的redis.conf里配了requirepass,ACL 系统仍然是生效的。实际上,requirepass本质上是在给default用户设置密码。如果你同时配置了 ACL 文件和requirepass,可能会产生预期外的行为,后面我会专门说一下这个坑。

理解了这些基础概念,我们接下来看规则语法。ACL 规则语法分两种大方向:一种是on/off控制用户是否启用,另一种是+命令/-命令控制命令权限,还有~keypattern控制键空间访问,以及>密码设置密码。

下面这张表把常用的规则语法整理了一下,建议收藏:

规则语法含义示例
on/off启用 / 禁用用户on
>password设置明文密码>123456
#hash设置 SHA256 哈希密码#5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
nopass允许无密码登录nopass
+<command>允许执行某个命令+get
-<command>禁止执行某个命令-flushall
+@<category>允许某个命令类别(如+@read+@read
-@<category>禁止某个命令类别-@admin
allcommands/+@all允许所有命令allcommands
nocommands/-@all禁止所有命令nocommands
~<pattern>允许访问匹配该 pattern 的 key~order:*
%R~<pattern>只允许读特定 pattern 的 key%R~user:*
%W~<pattern>只允许写特定 pattern 的 key%W~user:*
allkeys/~*允许访问所有 keyallkeys
resetkeys清除所有 key 权限resetkeys
reset重置用户到默认状态reset

有一个概念容易被新手忽略:+@<category>命令类别。Redis 把几百个命令分成了若干组,比如read(读类命令)、write(写类命令)、admin(管理类命令)、keyspace(键空间类)、dangerous(危险类)等。你不需要一条一条去加,直接按类别授权效率高很多。可以用ACL CAT命令查看所有分类,后面会讲。

3. 一批开箱即用的 ACL 命令,建议全部掌握

ACL 的命令并不复杂,我来按功能分类过一遍,每一个我都会配上实际返回结果,方便你对照着看。

3.1 查询类:ACL WHOAMIACL LISTACL CATACL GETUSER

这三个是排查问题最常用的。

ACL WHOAMI:查看当前连接的用户名。这个最简单,也最实用。有时候你配了半天权限,连上去不知道自己是哪个用户,跑一下就知道了。

127.0.0.1:6379> ACL WHOAMI "default"

ACL LIST:列出所有用户及其权限规则。请记住,这里显示的规则是标准化的、脱敏后的格式,密码不会明文展示,而是显示#<hash>或者<>占位符。

127.0.0.1:6379> ACL LIST 1) "user default on nopass ~* &* +@all"

看到没,这行内容的含义是:用户default,状态为on(启用),nopass(无密码),~*(可以访问所有 key),&*(可以访问所有 Pub/Sub 频道),+@all(允许所有命令)。

ACL CAT:查看命令分类。不带参数是列出所有分类,带分类名是列出该分类下包含的所有命令。

127.0.0.1:6379> ACL CAT 1) "keyspace" 2) "read" 3) "write" 4) "set" 5) "sortedset" 6) "list" 7) "hash" 8) "string" 9) "bitmap" 10) "hyperloglog" 11) "geo" 12) "stream" 13) "pubsub" 14) "admin" 15) "fast" 16) "slow" 17) "blocking" 18) "dangerous" 19) "connection" 20) "transaction" 21) "scripting"

再看某个分类下具体有哪些命令:

127.0.0.1:6379> ACL CAT dangerous 1) "flushall" 2) "flushdb" 3) "keys" 4) "shutdown" 5) "debug" 6) "config" 7) "replicaof" 8) "monitor" 9) "sort" 10) "scan" 11) "migrate" 12) "restore" 13) "swapdb" 14) "pfdebug" 15) "replconf" 16) "client" 17) "lastsave"

这告诉我们,像flushallconfigkeysshutdown这种命令全都被归到了dangerous分类里。如果你的用户业务逻辑上完全不需要这些命令,一定要在规则里显式禁用-@dangerous我见过太多事故就是因为业务账号能执行KEYS *,在数据量大的时候直接把 Redis 卡死。

ACL GETUSER <username>:查看某个具体用户的全部信息。这个比ACL LIST更详细,会把规则拆开给你看。

127.0.0.1:6379> ACL GETUSER default 1) "flags" 2) 1) "on" 3) "passwords" 4) (empty array) 5) "commands" 6) "+@all" 7) "keys" 8) "~*" 9) "channels" 10) "&*" 11) "selectors" 12) (empty array)

注意这几个字段:flags是用户状态,passwords是密码哈希列表(nopass的会显示空数组),commands是命令权限,keys是键权限,channels是 Pub/Sub 频道权限。这些信息在排障时非常有用。

3.2 用户管理:ACL SETUSERACL DELUSER

这是 ACL 的核心操作命令。它的特点是:多次调用是叠加生效的,不是覆盖。也就是说你先ACL SETUSER john +get,再ACL SETUSER john ~user:*,两次规则会叠加。

创建用户并设置密码:

127.0.0.1:6379> ACL SETUSER alice on >alice123 ~user:* +@read +@write -flushall -flushdb -config -keys OK

这个命令的含义是:创建(或更新)用户alice,启用状态,密码设为alice123,允许访问所有以user:开头的 key,允许所有读类和写类命令,同时明确禁止flushallflushdbconfigkeys这几个危险命令。

然后我们看看这个用户现在的配置:

127.0.0.1:6379> ACL GETUSER alice 1) "flags" 2) 1) "on" 3) "passwords" 4) 1) "2b5b0e2c0f2ef0e2fed4d73a6f0cf7c3e0d56ede68d0d851d1ae6f0aabb9d5fa" 5) "commands" 6) "+@read +@write -flushall -flushdb -config -keys" 7) "keys" 8) "~user:*" 9) "channels" 10) "&*"

关于密码这里我重点提醒一句:ACL SETUSER之后,如果这个用户之前存在,且它原来有密码,你设置新密码后旧密码还能不能用?答案是可以的。ACL SETUSER user >newpass追加一个密码,而不是替换。如果你确定要清除旧密码,得用>newpass配上<oldpass才能移除。这一点非常容易踩坑,我用一个例子演示:

# 给 alice 追加一个新密码 127.0.0.1:6379> ACL SETUSER alice >newpass321 OK # 查看, 现在 alice 有两个密码 127.0.0.1:6379> ACL GETUSER alice ... "passwords" 1) "2b5b0e2c0f2ef0e2fed4d73a6f0cf7c3e0d56ede68d0d851d1ae6f0aabb9d5fa" # 这是 alice123 的哈希 2) "2420d16c41f4b5aaefde46af0d4e232d3cd82036802e146a4eb1ed18bc3e6ed5" # 这是 newpass321 的哈希

想要彻底把alice123这个老密码干掉:

127.0.0.1:6379> ACL SETUSER alice <alice123 OK

这样passwords里就只剩newpass321对应的哈希了。所以如果你有轮换密码的需求,><配套用,先加后减,就不会导致密码清空时用户突然失联。

删除用户:

127.0.0.1:6379> ACL DELUSER alice (integer) 1

返回1表示删除成功,返回0表示该用户不存在。

还有一个非常实用的技巧:你可以用ACL SETUSER来临时禁用账号,不需要删除

# 禁用 alice 127.0.0.1:6379> ACL SETUSER alice off OK # 此时 alice 无论密码对不对,都连不上 Redis

这个方案比删除好,因为配置都还在,恢复时只需要ACL SETUSER alice on即可。

3.3 其他辅助命令:ACL GENPASSACL SAVEACL LOADACL LOG

ACL GENPASS:快速生成一个强密码。不带参数时默认生成 64 字节的十六进制随机串,你也可以指定长度。

127.0.0.1:6379> ACL GENPASS "2d9ff276b10e6a2d1740a6fbc5d2d20f5f584e9337aa6d8b0a6ebfa1d0f668ab"

ACL SAVE:把当前内存中的 ACL 规则持久化到aclfile指定的文件里。这个命令在重启不丢配置的场景下非常关键。

127.0.0.1:6379> ACL SAVE OK

前提是你得在redis.conf里配置了aclfile /etc/redis/users.acl。如果没配,会报错:

(error) ERR This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.

ACL LOAD:从aclfile加载 ACL 规则,覆盖当前的规则配置。当你改了文件但不想重启 Redis 时,用这个命令。

ACL LOG:查看 ACL 相关的拒绝日志。这玩意儿在排查“为什么我的操作被拒绝”时太好用了,它会告诉你是什么命令、哪个用户、访问了哪个 key、被哪条规则拦下的。后面排障章节我再细讲。

4. 实战:从零配置一套可落地的 ACL 权限体系

理论说再多,不如跑一遍。这一节我会从真实场景出发,演示一套完整的 ACL 配置流程。场景设定是一个电商项目,Redis 里有三个业务方要接入:订单微服务、用户微服务、运营后台。要求是:

  • 订单微服务:只能读写order:*的 key,禁止所有管理命令。
  • 用户微服务:只能读写user:*的 key,禁止所有管理命令。
  • 运营后台:可以读所有 key,但只能写report:*的 key,并且不允许删库、不能执行KEYSFLUSHALLCONFIG这些危险命令。
  • 默认default用户只允许本机访问,其他客户端一律要求显式账号密码登录。

4.1 设计用户与规则映射

先把需求翻译成规则:

业务用户名允许访问 key命令权限密码
订单微服务srv_order~order:*+@read +@write -@dangerous随机生成
用户微服务srv_user~user:*+@read +@write -@dangerous随机生成
运营后台ops~*(读所有key),%W~report:*+@read -@dangerous +set随机生成
管理员default~*+@all改为强密码

需要说明的是,ops用户这里我用到了 Redis 6.0 加入的键权限选择器(key permission selector)。用%R~pattern%W~pattern可以分别限定读和写的 key 范围。~*是允许读所有 key,%W~report:*是只允许写report:*。注意规则顺序:先写读权限,再写写权限,语义更清晰。

4.2 逐条执行 ACL 配置

先给default用户设置一个强密码。这里要非常小心,一旦执行了下面的命令,当前连接如果验证过密码就没事,如果没设置过密码的旧客户端就会全部掉线。

127.0.0.1:6379> ACL SETUSER default on >Default_Admin_2024_Str0ng! OK

创建订单微服务账号。我习惯把密码存到环境变量或密钥管理平台,这里为了演示就直接写了:

127.0.0.1:6379> ACL SETUSER srv_order on >Ord3n_$rv_Pa55 ~order:* +@read +@write -@dangerous OK

这里解释一下规则顺序:on启用,>设密码,~order:*限定 key,+@read +@write放开读写命令,最后-@dangerous把危险命令一票否决。Redis 处理 ACL 规则的顺序很重要,后面的-@dangerous会覆盖前面的某条具体命令权限。如果有冲突,以后面的规则为准。

创建用户微服务账号:

127.0.0.1:6379> ACL SETUSER srv_user on >Us3r_$rv_Pa55 ~user:* +@read +@write -@dangerous OK

创建运营后台账号。这里注意我用到了%R%W选择器:

127.0.0.1:6379> ACL SETUSER ops on >Op5_Pa55_Adm1n %R~* %W~report:* +@read +set -@dangerous OK

解释一下:+set是允许 SET 命令,因为默认+@read并不包含SET%R~*允许读任何 key,%W~report:*只允许写report:*前缀的 key。

检查一下各用户配置:

127.0.0.1:6379> ACL GETUSER ops 1) "flags" 2) 1) "on" 3) "passwords" 4) 1) "1f9a1c4b9bbd606e6f374ac7bfb7739c1dfa12dcea09bc5d4a41b7e5cc1af397" 5) "commands" 6) "+@read +set -@dangerous" 7) "keys" 8) "~*" 9) "channels" 10) "&*" 11) "selectors" 12) 1) 1) "key" 2) "%W~report:*"

注意selectors里多了一条%W~report:*,这说明写入权限被单独隔离出来了。

4.3 验证配置效果

单独开一个 redis-cli 连接,用srv_order登录:

$ redis-cli --user srv_order --pass Ord3n_$rv_Pa55 127.0.0.1:6379> AUTH srv_order Ord3n_$rv_Pa55 OK 127.0.0.1:6379> SET order:10001 paid OK 127.0.0.1:6379> GET order:10001 "paid" # 尝试访问非授权 key,被拒绝 127.0.0.1:6379> GET user:10001 (error) NOPERM No permissions to access a key # 尝试执行危险命令,被拒绝 127.0.0.1:6379> FLUSHALL (error) NOPERM this user has no permissions to run the 'flushall' command

再看看运营后台账号的表现:

$ redis-cli --user ops --pass Op5_Pa55_Adm1n 127.0.0.1:6379> GET order:10001 "paid" # 写非 report 前缀的 key,被拒绝 127.0.0.1:6379> SET order:10001 refunded (error) NOPERM No permissions to access a key # 写 report 前缀的 key,成功 127.0.0.1:6379> SET report:20240601 total_orders 1000 OK

完美符合我们的预期。这套配置验证通过后,就可以放心交付给各个业务方使用了。

4.4 配置文件与持久化方案

ACL 配置有两种持久化方式,我强烈推荐你用第二种。

方案一:写在 redis.conf 里面

直接在配置文件底部加:

user default on >Default_Admin_2024_Str0ng! ~* &* +@all user srv_order on >Ord3n_$rv_Pa55 ~order:* +@read +@write -@dangerous user srv_user on >Us3r_$rv_Pa55 ~user:* +@read +@write -@dangerous user ops on >Op5_Pa55_Adm1n %R~* %W~report:* +@read +set -@dangerous

然后重启 Redis 或执行CONFIG REWRITE让配置生效并写回文件。

方案二:使用独立的 aclfile(强烈推荐)

redis.conf中指定:

aclfile /etc/redis/users.acl

然后在运行时用ACL SAVE将内存中的规则一次性写入文件。这样做的最大好处是:规则文件与主配置分离,权限变更不需要动 redis.conf,也不容易引入语法错误把主配置搞崩。我经历过一次在 redis.conf 里写错一个 ACL 规则导致整个 Redis 起不来的事故之后,就再也不用方案一了。

另外要提醒的是:CONFIG REWRITE不会重写aclfile指向的文件。如果同时配置了aclfile,修改 ACL 后想持久化,必须执行ACL SAVE。反过来,如果只写在 redis.conf 里,用CONFIG REWRITE也会把user指令写进去。这两条路不要混着走,不然容易出现配置和实际生效状态不一致的情况。

5. 高频踩坑与排查技巧实录

这块内容是真正要让你的运维生活变得更轻松的部分。ACL 上线初期,我前前后后踩了不下十个坑,这里挑几个最典型的分享出来。

5.1 坑一:requirepass和 ACL 混用导致 default 用户行为诡异

有些老项目的redis.conf里还留着requirepass foobared,然后你又在同一个文件里配置了user default on >newpassword ...。这两个配置同时存在时,requirepass foobared会被翻译成user default on >foobared ...。结果就是default用户拥有两个密码:一个是foobared,一个是newpassword,两个都能登录。

解决方案也很明确:要么彻底移除requirepass,只依赖 ACL;要么只保留requirepass,别再用 ACL 去设置default密码。我建议直接移除requirepass,全面切换到 ACL,一个体系管到底,脑子不累。

5.2 坑二:ACL SETUSER加密码是“追加”不是“覆盖”

这个前面已经提到了,再强调一次。很多人以为执行ACL SETUSER alice >newpass后旧密码自动失效,实际上不会。这会导致一种很尴尬的情况:离职人员的账号虽然你改了密码,但他原来的密码依然能登录。排查起来极具迷惑性,因为ACL GETUSER里会列出所有密码哈希。

我的检查习惯是:每次改密码后,都执行一次ACL GETUSER <user>,确认passwords字段里只剩预期的哈希条目。如果有多余的,用<oldpass把它删掉。

5.3 坑三:忘记授权 Pub/Sub 频道权限

Redis 6.0 的 ACL 默认&*是允许所有频道的,但如果你显式写了规则,比如ACL SETUSER alice on >pass ~* +@all -pubsub,然后 alice 订阅频道SUBSCRIBE news时报错:

(error) NOPERM No permissions to access a channel

这是因为 Pub/Sub 频道的权限是独立的,由&<pattern>控制。如果你希望某个用户只能订阅特定频道,可以这样配:

ACL SETUSER alice on >pass ~* &news:* +@all -pubsub +subscribe +psubscribe +unsubscribe +punsubscribe

不过这里要注意:SUBSCRIBEPSUBSCRIBE这些命令虽然属于pubsub分类,但默认情况下是不受-@pubsub影响的(否则可能会误伤正常的订阅场景)。我实际测试下来,6.0 的-@pubsub主要禁用的是PUBLISH,订阅命令需要单独用频道规则去限制。所以当你发现订阅时报 channel 权限错误,不要怀疑是命令被禁了,去查一下&规则。

5.4 坑四:ACL LOG级别不够,看不到想看的日志

ACL LOG默认只记录最近 10 条事件,而且日志在 Redis 重启后会清空。排查问题时如果需要更长时间窗口的数据,可以调大日志长度:

127.0.0.1:6379> CONFIG SET acllog-max-len 128 OK

这个配置是动态生效的,不需要重启。ACL LOG也支持按用户过滤,比如只看srv_order被拒绝的记录:

127.0.0.1:6379> ACL LOG 128 srv_order

5.5 常见故障速查表

现象可能原因解决方式
AUTH认证失败用户名不存在 / 密码错误 / 用户被offACL GETUSER <user>确认状态与密码;ACL SETUSER <user> on
执行命令提示NOPERM this user has no permissions to run the 'xxx' command命令未在规则中显式允许+xxx添加命令,或直接+@read+@write
访问 key 提示NOPERM No permissions to access a keykey 规则没覆盖检查~pattern是否匹配;如需区分读写,用%R~%W~
订阅频道提示NOPERM No permissions to access a channel频道规则&pattern未配置添加&频道前缀*
重启后 ACL 规则丢失未持久化配置aclfile,执行ACL SAVE
用长时间运行的客户端突然掉线且无法重连密码被修改或用户被off检查ACL GETUSER,更新客户端配置
default用户意外能访问所有 keydefault默认就是 allkeysACL SETUSER default resetkeys ~业务前缀:*收紧权限

5.6 排障时必须有的操作习惯

这里我掏心窝子分享几个日常习惯:

  1. 改完 ACL 先看ACL GETUSER,再测连接。连续执行ACL GETUSER看清楚commandskeyschannels三个字段,确认和预期一致再切流量。

  2. 生产环境变更前,先备份当前 ACL 配置。ACL LIST把输出保存到一个文件,万一改崩了可以直接ACL SETUSER一条条恢复,或者直接ACL LOAD加载备份的 aclfile。

  3. 危险命令不仅要禁,还要在监控里盯。就算禁用FLUSHALL,也别对运维安全掉以轻心,最好在 Redis 的审计日志或云监控里配置告警,一旦出现FLUSHALLCONFIG SET等危险命令就触发通知。

  4. 给每个服务单独的账号,禁止共享账号。如果两个业务共用一个账号,出问题的时候你根本没法定位是谁干的。ACL 的价值之一就是审计,账号隔离是审计的前提。

6. 权限设计的几条经验,省得你走弯路

最后说几条宏观层面的设计建议,是我在多个项目里沉淀下来的。

第一,遵循最小权限原则。这句话听着像废话,但执行起来很难。我给你一个具体可操作的标准:“这个用户如果完全不执行某条命令、完全访问不到某个 key,业务能不能正常跑?如果能,就把他这条权限删掉。” 很多团队上线时图省事,直接+@all,等出了事故再来补救,成本完全不一样。Redis 的命令分类已经帮你做好了基础过滤,至少要把@dangerous@admin这两个分类关掉。

第二,key 的命名规范比权限规则更值得投入。ACL 的~pattern强烈依赖 key 的命名规律。如果你的业务 key 五花八门,没有统一前缀,那你写 ACL 规则会非常痛苦。反过来,如果所有业务 key 都有清晰的前缀(order:*user:*session:*),规则写起来一目了然,安全性和可维护性都大幅提升。顺便说一句,这也是 Redis 最佳实践中一直强调的点。

第三,ACL 是权限控制,但不能替代网络隔离。我见过一些团队配置了 ACL 之后,就把 Redis 直接暴露在公网上,觉得“反正没密码进不来”。这是非常危险的认知。ACL 在 Redis 内部做权限控制,但网络层该做的防火墙、安全组、私有网络隔离一点都不能少。应用层安全永远是最后一道防线,而不是第一道。

第四,定期审查 ACL 配置。我建议每个季度过一遍ACL LIST,看看有没有长期不用的账号,有没有密码已经泄露但还没轮换的账号。线上环境账号多了之后,很容易出现僵尸账号,这是安全隐患。审查的时候可以对比应用部署清单,逐一确认每个账号是否还在用。

7. 一点个人体会

用了一个多月的 ACL,我最大的感受是:Redis 6.0 引入 ACL 并不是为了炫技,而是把企业级缓存/存储该有的安全底线给补齐了。以前要在一个 Redis 实例上做多业务隔离,只能靠多实例 + 网络策略,成本高、管理麻烦。现在一个实例就能做到相对清晰的权限切分,架构简化的同时安全性反而提升了。

我个人在实际操作中比较推荐“先小范围验证,再全量推广”的策略。可以先把某一个非核心业务的账号切到 ACL 上,观察一段时间,确认没有误伤生产请求,再把其他业务全部切换过来。毕竟权限配置这东西,写错了不像代码报错那么明显,它往往是“看似正常,但某个功能悄悄失灵”的状态,排查起来特别费劲。只有在验证充分、规则明确的情况下批量切换,才比较稳妥。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询