1. 为什么你的RocketMQ需要ACL权限控制?
大家好,我是老张,在消息队列这块摸爬滚打快十年了。记得去年我们团队就踩过一个坑:一个测试环境的RocketMQ集群,因为没做任何权限管控,被隔壁项目组的同学误操作,把线上几个核心Topic的消息给消费掉了,直接导致线上业务数据对不上,排查了大半天。这事儿之后,我们立刻在所有环境都上了ACL。
简单来说,ACL(Access Control List,访问控制列表) 就是RocketMQ的“门禁系统”。想象一下,你家的消息队列就像一个存放重要文件的仓库,以前谁都能进,现在你得给每个人发张门禁卡,规定谁能进、能进哪个房间、能看哪些文件。在RocketMQ里,这个“门禁卡”就是用户名密码,“房间”就是Topic和Consumer Group,“能干什么”就是发布(PUB)或订阅(SUB)的权限。
在4.9.2这个版本,RocketMQ的ACL功能已经比较成熟稳定了。它主要管三件事:
- 你是谁(用户):通过
accessKey和secretKey(类似账号密码)来识别身份。 - 你想动什么(资源):主要是Topic(主题)和Consumer Group(消费组)。
- 你能怎么动(权限):允许(PUB|SUB)、拒绝(DENY)、只发布(PUB)、只订阅(SUB)。
如果你正在负责一个多团队共用、或者有安全合规要求(比如等保)的项目,给RocketMQ加上ACL绝对是必选项。它能有效防止误操作、越权访问,甚至是恶意攻击,让整个消息链路更可控、更安全。接下来,我就手把手带你从零开始,把RocketMQ 4.9.2的ACL配置和Java客户端集成整个流程跑通。
2. 5分钟快速开启Broker端的ACL开关
开启ACL,首先得在Broker端进行配置。别怕,步骤很简单,主要就是改两个配置文件。我假设你已经有一套RocketMQ在运行了(单机或集群都行)。
2.1 第一步:修改broker.conf,打开ACL总开关
找到你的Broker配置文件,通常是在${ROCKETMQ_HOME}/conf目录下的broker.conf(或者你自定义的配置文件)。用vim或者你喜欢的编辑器打开它,找到或者添加下面这行核心配置:
# ACL总开关,设置为true才能启用权限控制
aclEnable=true
这个参数默认是false,所以不配就是“裸奔”状态。把它改成true,Broker就会开始对所有的连接请求进行权限校验。这里有个我踩过的坑:记得检查你的brokerIP1和namesrvAddr配置是否正确,尤其是集群环境,IP地址一定要配置成当前机器可被外部访问的地址,不然客户端连不上,排查起来很头疼。
一个完整的基础broker.conf配置示例可以参考下面这样,我把关键参数都加上了注释:
# ACL核心开关
aclEnable=true
# Broker对外的IP,非常重要!
brokerIP1=192.168.1.100
# NameServer地址,客户端靠这个找Broker
namesrvAddr=192.168.1.100:9876
# 是否允许自动创建Topic,生产环境建议false
autoCreateTopicEnable=false
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
# 文件清理时间
deleteWhen=04
fileReservedTime=48
broRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
改完之后,重启Broker使配置生效。你可以通过./mqbroker -c ../conf/broker.conf来指定配置文件启动,或者用nohup挂到后台。
2.2 第二步:详解plain_acl.yml权限规则文件
光打开开关还不够,你得告诉Broker具体的规则。规则定义在${ROCKETMQ_HOME}/conf/plain_acl.yml文件里。这个文件是YAML格式的,缩进特别重要,写错了Broker可能启动失败或者规则不生效。
这个文件结构清晰,主要分两大块:全局IP白名单和用户账户权限。我结合一个生产环境中常用的配置例子,给你拆开讲明白。
# 第一部分:全局IP白名单
# 这里列出的IP地址,可以直接访问所有资源,无需账号密码。通常用于集群内部机器(如Broker、NameServer之间)的通信。
globalWhiteRemoteAddresses:
- 10.10.103.* # 支持通配符,表示10.10.103网段的所有IP
- 192.168.1.100 # 也可以写具体的IP
- 192.168.1.101
# 第二部分:用户账户及权限定义
accounts:
# 第一个账户:普通应用账户,有严格的权限限制
- accessKey: AppUser1 # 用户名,客户端连接时使用
secretKey: MySecretPass123 # 密码,务必复杂一些
whiteRemoteAddress: 192.168.2.* # 此用户允许连接的客户端IP段(可选)
admin: false # 是否为管理员,false表示普通用户
defaultTopicPerm: DENY # 默认Topic权限:拒绝所有。这是最安全的做法,白名单制。
defaultGroupPerm: SUB # 默认Group权限:只允许订阅。通常消费组权限管理更宽松。
topicPerms: # 针对特定Topic的精细权限
- `OrderTopic=PUB|SUB` # 对OrderTopic有发布和订阅权限
- `PayTopic=SUB` # 对PayTopic只有订阅权限
- `AuditTopic=DENY` # 明确拒绝访问AuditTopic
groupPerms: # 针对特定Consumer Group的权限
- `OrderGroup=SUB`
- `PayGroup=PUB|SUB`
# 第二个账户:管理员账户,来自特定IP,拥有所有权限
- accessKey: Admin
secretKey: AdminPass456
whiteRemoteAddress: 192.168.1.50 # 限制管理员只能从特定IP登录
admin: true # 关键!设为true后,此用户无视下方所有topicPerms/groupPerms规则,拥有全部权限。
# 当admin为true时,下面的defaultTopicPerm等配置其实不生效了,但建议也配上,保持清晰
defaultTopicPerm: PUB|SUB
defaultGroupPerm: PUB|SUB
我来解释一下几个容易混淆的点:
admin: true:这是“超级管理员”标志。一旦设置,这个账号就能访问集群内所有的Topic和Group,topicPerms和groupPerms的配置对它无效。这个账号一定要保管好,并且用whiteRemoteAddress限制其来源IP。defaultTopicPerm和defaultGroupPerm:这是默认规则。当某个Topic或Group没有在下面的topicPerms或groupPerms列表中被明确提到时,就应用这个默认规则。我强烈建议将defaultTopicPerm设为DENY,采用“默认拒绝,显式允许”的白名单模式,安全性最高。- 权限值:
PUB: 只能发送消息到该Topic。SUB: 只能从该Topic订阅/消费消息。PUB|SUB或SUB|PUB: 既可以发送也可以消费。中间是竖线|。DENY: 明确拒绝任何操作。
whiteRemoteAddress:这个配置是用户级别的IP白名单。即使账号密码对了,但如果客户端的IP不在这里面,连接也会被拒绝。这为安全又加了一道锁。
配置文件写好之后,同样需要重启Broker。你可以通过查看Broker的启动日志,搜索“acl”关键字,确认ACL配置是否被成功加载。
3. 让RocketMQ-Console可视化工具也支持ACL
运维同学最喜欢用RocketMQ-Console(现在叫RocketMQ Dashboard)来查看队列情况、发测试消息。但默认的Console可不认识ACL,直接连会报错。我们需要对它进行改造。
3.1 获取支持ACL的Console版本
官方社区版本的Console可能没有预置ACL支持。这里有两个选择:
- 自行编译:从Apache RocketMQ的GitHub仓库下载
rocketmq-dashboard源码,在配置文件中加入ACL参数后打包。这对新手有点门槛。 - 使用已集成的版本:很多社区开发者已经做好了适配。你可以在一些开源仓库或技术博客找到打包好的JAR文件。注意:务必从可信来源获取,以免安全风险。
假设你已经拿到了一个支持ACL的rocketmq-dashboard-1.0.0-SNAPSHOT.jar。
3.2 配置Console连接启用ACL
运行Console时,需要通过JVM参数或配置文件传入ACL的账号密码。通常是通过application.properties或application.yml配置。这里以application.properties为例:
# ACL功能开关,必须为true
rocketmq.config.aclEnabled=true
# 填入你在plain_acl.yml中配置的具有admin权限或相应Topic/Group权限的账号
rocketmq.config.accessKey=Admin
rocketmq.config.secretKey=AdminPass456
# NameServer地址
rocketmq.config.namesrvAddr=192.168.1.100:9876
然后启动Console:
java -jar rocketmq-dashboard-1.0.0-SNAPSHOT.jar --spring.config.location=file:/your_path/application.properties
启动后,用浏览器访问http://your_server_ip:8080(默认端口8080),如果能看到集群和Topic信息,说明Console已经用ACL账号成功连接上了。这时候你在Console上进行的任何操作(如查看消息、发送测试消息)都会经过Broker的ACL校验。
4. Java客户端集成ACL:两种实战方式
Broker和Console都配好了,现在轮到我们的业务应用了。Java客户端集成ACL的核心,就是在创建生产者(Producer)或消费者(Consumer)时,注入一个包含凭证(SessionCredentials)的钩子(RPCHook)。
4.1 基础方式:手动创建AclClientRPCHook
这种方式最直接,适合对RocketMQ原生API比较熟悉的场景。你需要用到org.apache.rocketmq.acl.common.AclClientRPCHook和org.apache.rocketmq.acl.common.SessionCredentials这两个类。
首先,确保你的Maven依赖包含了ACL模块。对于RocketMQ 4.9.2,依赖如下:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.9.2</version>
</dependency>
<!-- ACL核心包 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-acl</artifactId>
<version>4.9.2</version>
</dependency>
然后,我写一个工具方法来创建这个RPCHook:
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.remoting.RPCHook;
public class AclUtils {
/**
* 创建ACL认证钩子
* @param accessKey 用户名,对应plain_acl.yml中的accessKey
* @param secretKey 密码,对应plain_acl.yml中的secretKey
* @return RPCHook
*/
public static RPCHook getAclRPCHook(String accessKey, String secretKey) {
// SessionCredentials对象封装了认证信息
SessionCredentials credentials = new SessionCredentials(accessKey, secretKey);
// AclClientRPCHook会在每次RPC调用时,自动将凭证信息附加到请求中
return new AclClientRPCHook(credentials);
}
}
接下来,在创建生产者和消费者时,把这个Hook设置进去:
ACL生产者示例:
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
public class AclSyncProducer {
public static void main(String[] args) throws Exception {
// 1. 创建生产者,传入组名和ACL Hook
DefaultMQProducer producer = new DefaultMQProducer(
"ProducerGroupName",
AclUtils.getAclRPCHook("AppUser1", "MySecretPass123") // 使用普通应用账号
);
// 2. 设置NameServer地址
producer.setNamesrvAddr("192.168.1.100:9876");
// 3. 启动生产者
producer.start();
for (int i = 0; i < 10; i++) {
// 4. 创建消息,指定Topic和Tag
Message msg = new Message(
"OrderTopic", // 这个Topic必须在AppUser1的topicPerms里有PUB或PUB|SUB权限
"TagA",
("Hello ACL RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
);
// 5. 发送消息
org.apache.rocketmq.client.producer.SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
// 6. 关闭生产者
producer.shutdown();
}
}
ACL消费者示例:
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class AclPushConsumer {
public static void main(String[] args) throws Exception {
// 1. 创建消费者,传入组名和ACL Hook
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
"ConsumerGroupName",
AclUtils.getAclRPCHook("AppUser1", "MySecretPass123"), // 使用同一个账号
new AllocateMessageQueueAveragely() // 负载均衡策略
);
// 2. 设置NameServer地址
consumer.setNamesrvAddr("192.168.1.100:9876");
// 3. 订阅Topic,这里可以使用Tag进行过滤
consumer.subscribe("OrderTopic", "*"); // 这个Topic必须在AppUser1的topicPerms里有SUB或PUB|SUB权限
// 4. 注册消息监听器
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
for (MessageExt msg : msgs) {
System.out.printf("收到消息: Topic=%s, MsgId=%s, Body=%s %n",
msg.getTopic(), msg.getMsgId(), new String(msg.getBody()));
}
// 返回消费成功状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
// 5. 启动消费者
consumer.start();
System.out.println("消费者已启动...");
// 保持运行
Thread.sleep(Long.MAX_VALUE);
consumer.shutdown();
}
}
关键点提醒:
- GroupName:消费者组名
ConsumerGroupName需要在你账号的groupPerms里有对应权限(例如SUB),否则会订阅失败。 - 权限匹配:生产者发消息到
OrderTopic,要求账号对OrderTopic有PUB权限;消费者从OrderTopic拉消息,要求账号对OrderTopic有SUB权限。两者必须匹配,否则会收到AclException。 - 错误处理:在实际编码中,一定要对
producer.send()和消费者启动过程进行异常捕获。常见的ACL错误码如10015表示权限验证失败,你需要根据错误信息检查账号、密码、Topic/Group权限是否正确。
4.2 进阶方式:Spring Boot Starter自动集成
如果你的项目是基于Spring Boot的,那么集成起来会更优雅。RocketMQ官方提供了rocketmq-spring-boot-starter,可以很方便地通过配置文件注入ACL信息。
首先,添加Spring Boot Starter依赖(注意版本匹配):
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.1</version> <!-- 请确认与RocketMQ客户端版本兼容 -->
</dependency>
然后,在application.yml中配置ACL:
rocketmq:
name-server: 192.168.1.100:9876 # NameServer地址
producer:
group: your-producer-group # 生产者组名
access-key: AppUser1 # ACL用户名
secret-key: MySecretPass123 # ACL密码
# 启用ACL
enable-msg-trace: false # 根据需要设置,非ACL相关
access-channel: LOCAL
consumer:
# 消费者配置类似,如果消费者和生产者在同一应用,且用同一个账号,可以只配producer部分。
# 如果消费者独立,或使用不同账号,则需要单独配置access-key和secret-key
配置完成后,Spring Boot会自动帮你创建已经注入好ACL凭证的RocketMQTemplate和DefaultMQPushConsumer等Bean。你在代码中直接注入使用即可,完全不用关心RPCHook的创建:
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
@Component
public class SpringBootAclProducer {
@Autowired
private RocketMQTemplate rocketMQTemplate; // 直接注入,已包含ACL信息
public void sendMessage() {
// 发送同步消息,destination格式为 `topicName:tagName`
String destination = "OrderTopic:TagA";
String messageBody = "Hello from Spring Boot with ACL!";
rocketMQTemplate.syncSend(destination, messageBody);
System.out.println("消息发送成功");
}
}
对于消费者,使用@RocketMQMessageListener注解即可,框架会自动处理ACL认证:
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
@Component
@RocketMQMessageListener(
topic = "OrderTopic",
consumerGroup = "YourSpringBootConsumerGroup",
accessKey = "AppUser1", // 可以在这里覆盖全局配置
secretKey = "MySecretPass123"
)
public class SpringBootAclConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("收到消息: " + message);
}
}
这种方式极大地简化了配置,是Spring Boot项目的首选。你只需要确保配置文件中的access-key和secret-key有访问目标Topic和Group的权限。
5. 避坑指南:ACL实战中常见的“坑”与解决方案
配置和集成说起来简单,但实际落地时总会遇到些意想不到的问题。我把我遇到过和从社区里看到的常见“坑”总结一下,希望能帮你少走弯路。
坑1:Broker启动失败,提示“The ACL config file [plain_acl.yml] doesn‘t exist”
- 原因:Broker在
conf目录下找不到plain_acl.yml文件。 - 解决:检查文件路径和名称是否正确。确保文件在Broker启动命令
-c参数指定的配置文件所在目录的conf子目录下。或者,你可以在broker.conf中通过aclConfigPath参数指定plain_acl.yml的绝对路径,例如:aclConfigPath=/home/rocketmq/conf/plain_acl.yml。
坑2:客户端连接成功,但发送/订阅时报错 CODE: 10015 DESC: authorization failed
- 原因:这是最典型的ACL权限错误。说明账号密码认证通过了,但对特定资源的操作权限不足。
- 排查步骤:
- 核对账号密码:确认客户端使用的
accessKey和secretKey与plain_acl.yml中配置的一致,注意大小写。 - 检查Topic/Group权限:确认该账号在
topicPerms和groupPerms中对目标资源有正确的权限(PUB, SUB, PUB|SUB)。特别注意defaultTopicPerm和defaultGroupPerm,如果是DENY,那么任何未被明确列出的资源都无法访问。 - 检查IP白名单:如果账号配置了
whiteRemoteAddress,请确认客户端的IP地址在允许的范围内。 - 检查admin标志:如果账号是
admin: true,那应该拥有所有权限。如果还报错,检查Broker日志看是否有其他异常。
- 核对账号密码:确认客户端使用的
坑3:RocketMQ-Console能连上,但看不到Topic/Group列表,或操作失败
- 原因:Console使用的账号权限不足。Console需要读取集群的元数据(如Topic列表、消费进度等),这需要较高的权限。
- 解决:为Console配置一个具有
admin: true权限的账号,或者至少确保其defaultTopicPerm和defaultGroupPerm是SUB(对于只读查看)或PUB|SUB,并且能访问相关的系统Topic(如TBW102,这是自动创建Topic时用的,如果autoCreateTopicEnable=false则问题不大)。
坑4:权限变更后不生效
- 原因:修改了
plain_acl.yml文件后,没有重启Broker。ACL配置是在Broker启动时加载到内存的,不支持热加载。 - 解决:每次修改
plain_acl.yml后,必须重启Broker节点。在生产环境,这意味着你需要规划好重启窗口,或者采用集群滚动重启的方式。
坑5:Plain_acl.yml格式错误导致Broker启动失败
- 原因:YAML文件对缩进(空格)非常敏感,用Tab键或者缩进不对都会导致解析失败。
- 解决:使用支持YAML语法的编辑器(如VS Code, IntelliJ IDEA),并确保使用空格缩进。可以将配置文件内容粘贴到在线的YAML校验工具中检查格式。Broker启动日志通常会给出具体的解析错误行号。
坑6:客户端日志没有明显错误,但消息就是发送不出去或收不到
- 原因:可能是网络问题、NameServer地址错误,或者ACL账号没有对应Topic的生产/消费权限。
- 排查:开启客户端的详细日志。在log4j或logback配置中,将
org.apache.rocketmq的日志级别设为DEBUG或INFO,可以看到更详细的连接、认证和权限检查日志。同时,查看Broker端的日志${ROCKETMQ_HOME}/logs/rocketmqlogs/acl.log,里面记录了每一次权限校验的详细结果。
把ACL配上,晚上睡觉都能踏实点。尤其是当你的RocketMQ集群开始承载核心业务流量时,这套权限体系就是最基本的安全护栏。从配置到集成,整个过程其实并不复杂,关键是要理解“用户-资源-权限”这个模型,并且规划好一套清晰的权限分配方案,比如按项目、按团队来划分Topic和Group的访问权。
1万+



被折叠的 条评论
为什么被折叠?



