EMQX Redis 授权兼容模式 v4 深度解析:无缝承接 EMQX 4.x ACL 数据
【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx
EMQX 5.x 的 Redis 授权(Authorization)新增了compatibility_mode = v4配置项,用于兼容 EMQX 4.x 时代写入 Redis 的旧版 ACL 数据。本文以本仓库中对应变更文档 changes/ee/fix-16730.en.md 为核心,结合emqx_auth_redis应用的源码实现与测试用例,深入讲解该兼容模式的工作原理、配置方法、旧版占位符与 ACL 值的映射规则,以及从 EMQX 4.x 平滑迁移到 5.x 的落地要点。读完本文,你将能够准确判断是否启用该模式、正确配置 Redis 授权,并理解底层占位符渲染与规则解析的完整链路。
一、为什么需要兼容模式:EMQX 4.x 与 5.x 的 ACL 数据格式差异
在 EMQX 4.x 时代,Redis 中存储的 ACL(访问控制列表)数据采用了与 5.x 截然不同的两种约定:
- 占位符语法不同:4.x 使用
%u(用户名)和%c(客户端 ID)作为占位符,例如查询命令HGETALL mqtt_acl:%u、主题过滤器pub/%u、sub/%c;而 5.x 使用${username}、${clientid}这类模板变量语法。 - 访问值编码不同:4.x 的规则值使用数字
1、2、3分别表示 subscribe(订阅)、publish(发布)和 all(全部);而 5.x 原生接受字符串subscribe、publish、all,以及包含action、qos、retain等字段的 JSON 对象。
如果直接沿用 4.x 的 Redis 数据,5.x 的 Redis 授权会因无法解析这些旧格式而拒绝匹配,导致客户端全部被拒(或依赖no_match的兜底策略)。兼容模式正是为了解决这一迁移痛点而生。
二、compatibility_mode 配置项:定义、取值与默认行为
该配置项在 emqx_authz_redis_schema.erl 中定义,属于 Redis 授权数据源的专属字段:
compatibility_mode() -> ?HOCON(hoconsc:enum([disabled, v4]), #{ required => false, default => disabled, desc => ?DESC(compatibility_mode), example => v4 }).关键信息如下:
| 属性 | 取值 | 说明 |
|---|---|---|
| 枚举值 | disabled/v4 | 仅支持这两个取值 |
| 默认值 | disabled | 默认关闭,保证已有 Redis 授权行为完全不变 |
| 是否必填 | 否 | 不配置即视为disabled |
i18n 描述文件 emqx_authz_redis_schema.hocon 中给出了官方语义:
Redis ACL compatibility mode. Set to
v4to accept legacy ACL values1|2|3and placeholders%u/%c.
配置方式(HOCON 格式,位于authorization.sources下):
authorization { sources = [ { type = redis enable = true redis_type = single server = "127.0.0.1:6379" database = 1 password = "public" cmd = "HGETALL mqtt_acl:%u" compatibility_mode = v4 } ] }说明:测试代码 emqx_authz_redis_SUITE.erl 中的
raw_redis_authz_config/0提供了最小可用配置骨架(type=redis、redis_type=single、cmd等),可作为手工编写配置时的参照。该配置同样支持redis_sentinel、redis_cluster两种部署形态,对应redis_type = sentinel / cluster。
三、兼容模式下的两大核心行为
3.1 旧版占位符 %u/%c 的自动转换
启用compatibility_mode = v4后,cmd命令模板与查询返回的主题过滤器中的%u、%c会被自动转换为 5.x 的模板变量语法${username}、${clientid}。这一逻辑由 emqx_authz_redis.erl 中的normalize_legacy_placeholders/2实现:
normalize_legacy_placeholders(Bin, ?ACL_COMPAT_MODE_V4) when is_binary(Bin) -> binary:replace( binary:replace(Bin, <<"%u">>, <<"${username}">>, [global]), <<"%c">>, <<"${clientid}">>, [global] ); normalize_legacy_placeholders(Value, _ACLCompatibilityMode) -> Value.从源码可以看到两个关键细节:
- 替换是全局的(
[global]):同一字符串中出现的所有%u/%c都会被替换,且先替换%u再替换%c。 - 仅在 v4 模式下生效:其他任何取值都原样返回,不会做任何转换。
该函数被调用在两个位置:
new_state/2初始化状态时,对cmd模板字符串做归一化,再进入parse_cmd/1的模板解析流程;do_authorize/5中处理每条返回记录时,对哈希表的 key(即主题过滤器)做归一化。
也就是说,占位符转换同时作用于查询命令和查询结果中的主题过滤器。例如 4.x 的HGETALL mqtt_acl:%u在 v4 模式下等效于 5.x 的HGETALL mqtt_acl:${username},而存储为pub/%u的主题过滤器会被当作pub/${username}参与匹配。
3.2 旧版 ACL 访问值 1|2|3 的映射
启用 v4 模式后,查询结果中的规则值1、2、3会被映射为订阅、发布、全部三种动作。该逻辑位于parse_rule/2:
parse_rule(<<"1">>, ?ACL_COMPAT_MODE_V4) -> {ok, #{<<"action">> => <<"subscribe">>}}; parse_rule(<<"2">>, ?ACL_COMPAT_MODE_V4) -> {ok, #{<<"action">> => <<"publish">>}}; parse_rule(<<"3">>, ?ACL_COMPAT_MODE_V4) -> {ok, #{<<"action">> => <<"all">>}}; parse_rule(<<"publish">>, _ACLCompatibilityMode) -> {ok, #{<<"action">> => <<"publish">>}}; parse_rule(<<"subscribe">>, _ACLCompatibilityMode) -> {ok, #{<<"action">> => <<"subscribe">>}}; parse_rule(<<"all">>, _ACLCompatibilityMode) -> {ok, #{<<"action">> => <<"all">>}};映射关系总结如下:
| Redis 中存储的规则值 | 兼容模式(v4)下的含义 | 5.x 原生等价写法 |
|---|---|---|
1 | subscribe(订阅) | subscribe |
2 | publish(发布) | publish |
3 | all(全部) | all |
继续往下读源码还可以看到,除数字与字符串外,parse_rule/2还支持 JSON 对象形式(如{"action":"publish","qos":1,"retain":true}),这部分在 v4 与非 v4 模式下行为一致。若返回的值既不是1|2|3、也不是publish/subscribe/all、更不是合法 JSON 对象,则会被判定为非法规则并记录错误日志(parse_rule_error),该条规则按不匹配处理。
四、完整工作链路:从配置到鉴权决策
为了看清兼容模式在整条授权链路中的位置,这里梳理 emqx_authz_redis.erl 的关键调用关系:
- 资源生命周期回调:
create/1、update/2、destroy/1负责创建/更新/销毁 Redis 连接资源;其中new_state/2会读取compatibility_mode并通过normalize_acl_compatibility_mode/1归一化取值(<<"v4">>或v4原子都识别为 v4,其余一律视为disabled),随后对cmd做占位符归一化与模板解析。 - 授权回调
authorize/4:首先用emqx_auth_template:render_deep_for_raw/2基于当前连接上下文(用户名、客户端 ID 等)渲染出最终 Redis 命令,再通过emqx_authz_utils:cached_simple_sync_query/3走缓存查询;查询失败时记录错误日志并按后端失败策略(取决于安全配置档)返回结果。 - 规则匹配
do_authorize/5:对查询返回的每一对<主题过滤器, 规则值>调用parse_rule/2解析,构造出带permission=allow的规则映射,最终交给emqx_authz_utils:authorize_with_row/6完成与目标 Topic 的匹配,命中则返回{matched, Permission},否则继续遍历下一条。
另外值得注意的是validate_cmd/1对命令的约束:Redis 授权仅接受HGETALL与HMGET两种命令,且命令不能为空。这是 Redis 作为授权数据源时查询形态的硬性限制,配置cmd时需遵守。
五、测试用例如何验证兼容行为
兼容模式的正确性在 emqx_authz_redis_SUITE.erl 中有三组针对性用例,可直接作为行为规格阅读:
1.v4_acl_values(数字值映射)
预先写入HMSET acl:username a 1 b 2 d 3,cmd为HGETALL acl:${username},compatibility_mode => v4。校验结果:
- 主题
a:发布 deny、订阅 allow(1→ subscribe) - 主题
b:发布 allow、订阅 deny(2→ publish) - 主题
d:发布 allow、订阅 allow(3→ all)
2.v4_placeholders_in_cmd_and_topic_filter(占位符转换)
预先写入HMSET mqtt_acl:username pub/%u 2 sub/%c 1,cmd为HGETALL mqtt_acl:%u,compatibility_mode => v4。校验结果:
- 客户端以
username连接时,允许向pub/username发布(命令中的%u被替换,返回的主题过滤器pub/%u也被解析为pub/${username}); - 允许订阅
sub/clientid(%c同理转换为${clientid})。
3.v4_acl_values_ignored_without_compat(默认模式行为不变)
同样的1|2|3数据,但不设置compatibility_mode。校验结果为所有主题的发布、订阅请求全部 deny——即未启用兼容模式时,1|2|3会被当作非法规则忽略,原有行为完全不受影响。这正是"默认保持禁用,现有 Redis 授权行为不变"这一设计承诺的直接证据。
六、迁移实践与注意事项
结合变更文档、源码与测试,从 EMQX 4.x 迁移到 5.x 时可参考以下要点:
- 确认数据格式:检查 Redis 中 ACL 数据的命令模板是否使用了
%u/%c占位符,规则值是否为1|2|3。只要命中其中一种,就需要考虑启用兼容模式。 - 启用方式:在
authorization.sources的 redis 数据源中设置compatibility_mode = v4并保留原cmd(如HGETALL mqtt_acl:%u),无需改写 Redis 数据本身即可让旧数据重新生效。 - 兼容性影响面:v4 模式会同时改变
cmd模板与查询结果主题过滤器的占位符解析,因此务必在启用后回归验证:旧的%u数据是否会与使用${username}的新数据混用(两者在 v4 模式下等价,可共存)。 - 命令约束:无论是否启用兼容模式,
cmd只能是HGETALL或HMGET;自定义命令会导致数据源创建或更新失败。 - 安全兜底:若旧 ACL 数据中存在非法规则值(如拼写错误的
pub),该条规则会被跳过并打印parse_rule_error日志,最终是否放行取决于no_match与安全配置档(legacy/hardened)的设定,建议迁移前先在小范围灰度验证。
七、相关文件索引
- 变更文档:changes/ee/fix-16730.en.md,并收录于 changes/6.1.1.en.md 的 Access Control 章节
- 配置 schema 定义:emqx_authz_redis_schema.erl
- 核心实现(占位符转换、规则解析、授权回调):emqx_authz_redis.erl
- 行为验证测试:emqx_authz_redis_SUITE.erl
- 配置项语义描述:emqx_authz_redis_schema.hocon
【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考