This is an automated email from the ASF dual-hosted git repository.

jinrongtong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new a4e02f4  [Enhancement] ACL can add rules in clusters (#340)
a4e02f4 is described below

commit a4e02f472fb666e8f149bd07ebbeff8c7e6629ba
Author: Crazylychee <[email protected]>
AuthorDate: Tue Jul 8 10:46:25 2025 +0800

    [Enhancement] ACL can add rules in clusters (#340)
    
    * [Enhancement] ACL can add rules in clusters and fix ISSUE #297
    
    * rollback the yml change
---
 docs/1_0_0/UserGuide_CN.md                         |   5 +-
 frontend-new/src/api/remoteApi/remoteApi.js        |  42 +-
 frontend-new/src/components/acl/SubjectInput.jsx   |  18 +-
 .../src/components/consumer/ClientInfoModal.jsx    | 117 ++++-
 .../components/consumer/ConsumerDetailModal.jsx    |  88 +++-
 frontend-new/src/i18n/index.js                     |  35 ++
 frontend-new/src/pages/Acl/acl.jsx                 | 548 ++++++++++-----------
 frontend-new/src/pages/Consumer/consumer.jsx       |  36 +-
 frontend-new/src/pages/Message/message.jsx         |   3 +-
 .../dashboard/controller/AclController.java        |  31 +-
 .../apache/rocketmq/dashboard/model/AclInfo.java   | 209 ++++++++
 .../rocketmq/dashboard/model/PolicyRequest.java    |   3 +-
 .../model/{PolicyRequest.java => UserInfoDto.java} |  32 +-
 .../dashboard/model/request/UserCreateRequest.java |   3 +-
 .../dashboard/model/request/UserUpdateRequest.java |   3 +-
 .../rocketmq/dashboard/service/AclService.java     |  17 +-
 .../dashboard/service/impl/AclServiceImpl.java     | 291 +++++++----
 .../service/impl/ConsumerServiceImpl.java          |  25 +-
 .../service/provider/UserInfoProviderImpl.java     |   5 +-
 .../dashboard/controller/AclControllerTest.java    | 223 +++++----
 20 files changed, 1124 insertions(+), 610 deletions(-)

diff --git a/docs/1_0_0/UserGuide_CN.md b/docs/1_0_0/UserGuide_CN.md
index 49de4e3..25afa53 100755
--- a/docs/1_0_0/UserGuide_CN.md
+++ b/docs/1_0_0/UserGuide_CN.md
@@ -94,8 +94,9 @@
 
 ## ACL2.0管理界面
 
-- 支持根据broker地址的acl规则的查询
+- 支持根据集群名字或者broker地址的acl规则的查询
 - acl规则的修改、新增、删除、查找
+- 如果只是选取了集群名字,那么查询的acl列表将会取交集,如果选取了brokerName,就会返回该broker的acl列表。
 - (不再支持acl1.0)
 
 ![image-20250706145313629](UserGuide_CN/image-20250706145313629.png)
@@ -188,4 +189,4 @@ rolePerms:
     - /monitor/*
     ....
 ```
-* 
3.前端页面显示上,为了更好区分普通用户和admin用户权限,关于资源的删除、更新等操作按钮不对普通用户角色显示,如果要执行资源相关操作,需要退出使用admin角色登录。
\ No newline at end of file
+* 
3.前端页面显示上,为了更好区分普通用户和admin用户权限,关于资源的删除、更新等操作按钮不对普通用户角色显示,如果要执行资源相关操作,需要退出使用admin角色登录。
diff --git a/frontend-new/src/api/remoteApi/remoteApi.js 
b/frontend-new/src/api/remoteApi/remoteApi.js
index af75920..d2390f5 100644
--- a/frontend-new/src/api/remoteApi/remoteApi.js
+++ b/frontend-new/src/api/remoteApi/remoteApi.js
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 const appConfig = {
-    apiBaseUrl: 'http://localhost:8082' // 请替换为你的实际 API Base URL
+    apiBaseUrl: 'http://localhost:8082'
 };
 
 let _redirectHandler = null;
@@ -74,34 +74,36 @@ const remoteApi = {
         }
     },
 
-    listUsers: async (brokerAddress) => {
+    listUsers: async (brokerName, clusterName) => {
         const params = new URLSearchParams();
-        if (brokerAddress) params.append('brokerAddress', brokerAddress);
+        if (brokerName) params.append('brokerName', brokerName);
+        if (clusterName) params.append('clusterName', clusterName);
         const response = await 
remoteApi._fetch(remoteApi.buildUrl(`/acl/users.query?${params.toString()}`));
         return await response.json();
     },
 
-    createUser: async (brokerAddress, userInfo) => {
+    createUser: async (brokerName, userInfo, clusterName) => {
         const response = await 
remoteApi._fetch(remoteApi.buildUrl('/acl/createUser.do'), {
             method: 'POST',
             headers: {'Content-Type': 'application/json'},
-            body: JSON.stringify({brokerAddress, userInfo})
+            body: JSON.stringify({brokerName, userInfo, clusterName})
         });
-        return await response.json(); // 返回字符串消息
+        return await response.json();
     },
 
-    updateUser: async (brokerAddress, userInfo) => {
+    updateUser: async (brokerName, userInfo, clusterName) => {
         const response = await 
remoteApi._fetch(remoteApi.buildUrl('/acl/updateUser.do'), {
             method: 'POST',
             headers: {'Content-Type': 'application/json'},
-            body: JSON.stringify({brokerAddress, userInfo})
+            body: JSON.stringify({brokerName, userInfo, clusterName})
         });
         return await response.json();
     },
 
-    deleteUser: async (brokerAddress, username) => {
+    deleteUser: async (brokerName, username, clusterName) => {
         const params = new URLSearchParams();
-        if (brokerAddress) params.append('brokerAddress', brokerAddress);
+        if (brokerName) params.append('brokerName', brokerName);
+        if (clusterName) params.append('clusterName', clusterName);
         params.append('username', username);
         const response = await 
remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteUser.do?${params.toString()}`), 
{
             method: 'DELETE'
@@ -109,38 +111,40 @@ const remoteApi = {
         return await response.json();
     },
 
-    // --- ACL 权限相关 API ---
-    listAcls: async (brokerAddress, searchParam) => {
+    listAcls: async (brokerName, searchParam, clusterName) => {
         const params = new URLSearchParams();
-        if (brokerAddress) params.append('brokerAddress', brokerAddress);
+        if (brokerName) params.append('brokerName', brokerName);
+        if (clusterName) params.append('clusterName', clusterName);
         if (searchParam) params.append('searchParam', searchParam);
+        if (searchParam != null) console.log(1111)
         const response = await 
remoteApi._fetch(remoteApi.buildUrl(`/acl/acls.query?${params.toString()}`));
         return await response.json();
     },
 
-    createAcl: async (brokerAddress, subject, policies) => {
+    createAcl: async (brokerName, subject, policies, clusterName) => {
         const response = await 
remoteApi._fetch(remoteApi.buildUrl('/acl/createAcl.do'), {
             method: 'POST',
             headers: {'Content-Type': 'application/json'},
-            body: JSON.stringify({brokerAddress, subject, policies})
+            body: JSON.stringify({brokerName, subject, policies, clusterName})
         });
         return await response.json();
     },
 
-    updateAcl: async (brokerAddress, subject, policies) => {
+    updateAcl: async (brokerName, subject, policies, clusterName) => {
         const response = await 
remoteApi._fetch(remoteApi.buildUrl('/acl/updateAcl.do'), {
             method: 'POST',
             headers: {'Content-Type': 'application/json'},
-            body: JSON.stringify({brokerAddress, subject, policies})
+            body: JSON.stringify({brokerName, subject, policies, clusterName})
         });
         return await response.json();
     },
 
-    deleteAcl: async (brokerAddress, subject, resource) => {
+    deleteAcl: async (brokerName, subject, resource, clusterName) => {
         const params = new URLSearchParams();
-        if (brokerAddress) params.append('brokerAddress', brokerAddress);
+        if (brokerName) params.append('brokerAddress', brokerName);
         params.append('subject', subject);
         if (resource) params.append('resource', resource);
+        if (clusterName) params.append('clusterName', clusterName);
         const response = await 
remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteAcl.do?${params.toString()}`), {
             method: 'DELETE'
         });
diff --git a/frontend-new/src/components/acl/SubjectInput.jsx 
b/frontend-new/src/components/acl/SubjectInput.jsx
index 4c2ee61..a3b6dc7 100644
--- a/frontend-new/src/components/acl/SubjectInput.jsx
+++ b/frontend-new/src/components/acl/SubjectInput.jsx
@@ -20,13 +20,13 @@ import React, {useEffect, useState} from 'react';
 
 const {Option} = Select;
 
-// Subject 类型枚举
+
 const subjectTypes = [
     {value: 'User', label: 'User'},
 ];
 
-const SubjectInput = ({value, onChange, disabled}) => {
-    // 解析传入的 value,将其拆分为 type 和 name
+const SubjectInput = ({value, onChange, disabled, t}) => {
+
     const parseValue = (val) => {
         if (!val || typeof val !== 'string') {
             return {type: subjectTypes[0].value, name: ''}; // 默认值
@@ -35,27 +35,25 @@ const SubjectInput = ({value, onChange, disabled}) => {
         if (parts.length === 2 && subjectTypes.some(t => t.value === 
parts[0])) {
             return {type: parts[0], name: parts[1]};
         }
-        return {type: subjectTypes[0].value, name: val}; // 如果格式不匹配,将整个值作为 
name,类型设为默认
+        return {type: subjectTypes[0].value, name: val};
     };
 
     const [currentType, setCurrentType] = useState(() => 
parseValue(value).type);
     const [currentName, setCurrentName] = useState(() => 
parseValue(value).name);
 
-    // 当外部 value 变化时,更新内部状态
     useEffect(() => {
         const parsed = parseValue(value);
         setCurrentType(parsed.type);
         setCurrentName(parsed.name);
     }, [value]);
 
-    // 当类型或名称变化时,通知 Form.Item
     const triggerChange = (changedType, changedName) => {
         if (onChange) {
-            // 只有当名称不为空时才组合,否则只返回类型或空字符串
+
             if (changedName) {
                 onChange(`${changedType}:${changedName}`);
-            } else if (changedType) { // 如果只选择了类型,但名称为空,则不组合
-                onChange(''); // 或者根据需求返回 'User:' 等,但通常这种情况下不应该有值
+            } else if (changedType) {
+                onChange('');
             } else {
                 onChange('');
             }
@@ -91,7 +89,7 @@ const SubjectInput = ({value, onChange, disabled}) => {
                 style={{width: '70%'}}
                 value={currentName}
                 onChange={onNameChange}
-                placeholder="请输入名称 (例如: yourUsername)"
+                placeholder={t.PLEASE_INPUT_NAME}
                 disabled={disabled}
             />
         </Input.Group>
diff --git a/frontend-new/src/components/consumer/ClientInfoModal.jsx 
b/frontend-new/src/components/consumer/ClientInfoModal.jsx
index 2821cbf..4cf5de5 100644
--- a/frontend-new/src/components/consumer/ClientInfoModal.jsx
+++ b/frontend-new/src/components/consumer/ClientInfoModal.jsx
@@ -16,15 +16,15 @@
  */
 
 import React, {useEffect, useState} from 'react';
-import {Modal, Spin, Table} from 'antd';
+import {Descriptions, Modal, Spin, Table, Tag, Tooltip} from 'antd';
 import {remoteApi} from '../../api/remoteApi/remoteApi';
 import {useLanguage} from '../../i18n/LanguageContext';
 
+
 const ClientInfoModal = ({visible, group, address, onCancel}) => {
     const {t} = useLanguage();
     const [loading, setLoading] = useState(false);
     const [connectionData, setConnectionData] = useState(null);
-    const [subscriptionData, setSubscriptionData] = useState(null);
 
     useEffect(() => {
         const fetchData = async () => {
@@ -33,10 +33,8 @@ const ClientInfoModal = ({visible, group, address, 
onCancel}) => {
             setLoading(true);
             try {
                 const connResponse = await 
remoteApi.queryConsumerConnection(group, address);
-                const topicResponse = await 
remoteApi.queryTopicByConsumer(group, address);
 
                 if (connResponse.status === 0) 
setConnectionData(connResponse.data);
-                if (topicResponse.status === 0) 
setSubscriptionData(topicResponse.data);
             } finally {
                 setLoading(false);
             }
@@ -46,53 +44,118 @@ const ClientInfoModal = ({visible, group, address, 
onCancel}) => {
     }, [visible, group, address]);
 
     const connectionColumns = [
-        {title: 'ClientId', dataIndex: 'clientId'},
-        {title: 'ClientAddr', dataIndex: 'clientAddr'},
-        {title: 'Language', dataIndex: 'language'},
-        {title: 'Version', dataIndex: 'versionDesc'},
+        {
+            title: t.CLIENTID, dataIndex: 'clientId', key: 'clientId', width: 
220, ellipsis: true,
+            render: (text) => (
+                <Tooltip title={text}>
+                    {text}
+                </Tooltip>
+            )
+        },
+        {title: t.CLIENTADDR, dataIndex: 'clientAddr', key: 'clientAddr', 
width: 150, ellipsis: true},
+        {title: t.LANGUAGE, dataIndex: 'language', key: 'language', width: 
100},
+        {title: t.VERSION, dataIndex: 'versionDesc', key: 'versionDesc', 
width: 100},
     ];
 
     const subscriptionColumns = [
-        {title: 'Topic', dataIndex: 'topic'},
-        {title: 'SubExpression', dataIndex: 'subString'},
+        {
+            title: t.TOPIC, dataIndex: 'topic', key: 'topic', width: 250, 
ellipsis: true,
+            render: (text) => (
+                <Tooltip title={text}>
+                    {text}
+                </Tooltip>
+            )
+        },
+        {title: t.SUBSCRIPTION_EXPRESSION, dataIndex: 'subString', key: 
'subString', width: 150, ellipsis: true},
+        {
+            title: t.EXPRESSION_TYPE, dataIndex: 'expressionType', key: 
'expressionType', width: 120,
+            render: (text) => <Tag color="blue">{text}</Tag>
+        },
+        // --- Added Columns for TagsSet and CodeSet ---
+        {
+            title: t.TAGS_SET, // Ensure t.TAGS_SET is defined in your 
language file
+            dataIndex: 'tagsSet',
+            key: 'tagsSet',
+            width: 150,
+            render: (tags) => (
+                tags && tags.length > 0 ? (
+                    <Tooltip title={tags.join(', ')}>
+                        {tags.map((tag, index) => (
+                            <Tag key={index} color="default">{tag}</Tag>
+                        ))}
+                    </Tooltip>
+                ) : 'N/A'
+            ),
+            ellipsis: true,
+        },
+        {
+            title: t.CODE_SET, // Ensure t.CODE_SET is defined in your 
language file
+            dataIndex: 'codeSet',
+            key: 'codeSet',
+            width: 150,
+            render: (codes) => (
+                codes && codes.length > 0 ? (
+                    <Tooltip title={codes.join(', ')}>
+                        {codes.map((code, index) => (
+                            <Tag key={index} color="default">{code}</Tag>
+                        ))}
+                    </Tooltip>
+                ) : 'N/A'
+            ),
+            ellipsis: true,
+        },
+        // --- End of Added Columns ---
+        {title: t.SUB_VERSION, dataIndex: 'subVersion', key: 'subVersion', 
width: 150},
     ];
 
+    const formattedSubscriptionData = connectionData?.subscriptionTable
+        ? Object.keys(connectionData.subscriptionTable).map(key => ({
+            ...connectionData.subscriptionTable[key],
+            key: key,
+        }))
+        : [];
+
     return (
         <Modal
-            title={`[${group}]${t.CLIENT}`}
+            title={`[${group}] ${t.CLIENT_INFORMATION}`}
             visible={visible}
             onCancel={onCancel}
             footer={null}
-            width={800}
+            width={1200} // Increased width to accommodate more columns
         >
             <Spin spinning={loading}>
                 {connectionData && (
                     <>
+                        <Descriptions bordered column={2} 
title={t.CONNECTION_OVERVIEW} style={{marginBottom: 20}}>
+                            <Descriptions.Item label={t.CONSUME_TYPE}>
+                                <Tag 
color="green">{connectionData.consumeType}</Tag>
+                            </Descriptions.Item>
+                            <Descriptions.Item label={t.MESSAGE_MODEL}>
+                                <Tag 
color="geekblue">{connectionData.messageModel}</Tag>
+                            </Descriptions.Item>
+                            <Descriptions.Item label={t.CONSUME_FROM_WHERE}>
+                                <Tag 
color="purple">{connectionData.consumeFromWhere}</Tag>
+                            </Descriptions.Item>
+                        </Descriptions>
+
+                        <h3>{t.CLIENT_CONNECTIONS}</h3>
                         <Table
                             columns={connectionColumns}
                             dataSource={connectionData.connectionSet}
                             rowKey="clientId"
                             pagination={false}
+                            scroll={{x: 'max-content'}}
+                            style={{marginBottom: 20}}
                         />
-                        <h4>{t.SUBSCRIPTION}</h4>
+
+                        <h3>{t.CLIENT_SUBSCRIPTIONS}</h3>
                         <Table
                             columns={subscriptionColumns}
-                            dataSource={
-                                subscriptionData?.subscriptionTable
-                                    ? 
Object.entries(subscriptionData.subscriptionTable).map(([topic, detail]) => ({
-                                        topic,
-                                        ...detail,
-                                    }))
-                                    : []
-                            }
-                            rowKey="topic"
+                            dataSource={formattedSubscriptionData}
+                            rowKey="key"
                             pagination={false}
-                            locale={{
-                                emptyText: loading ? <Spin size="small"/> : 
t.NO_DATA
-                            }}
+                            scroll={{x: 'max-content'}}
                         />
-                        <p>ConsumeType: {connectionData.consumeType}</p>
-                        <p>MessageModel: {connectionData.messageModel}</p>
                     </>
                 )}
             </Spin>
diff --git a/frontend-new/src/components/consumer/ConsumerDetailModal.jsx 
b/frontend-new/src/components/consumer/ConsumerDetailModal.jsx
index 4c89acd..b46ffe7 100644
--- a/frontend-new/src/components/consumer/ConsumerDetailModal.jsx
+++ b/frontend-new/src/components/consumer/ConsumerDetailModal.jsx
@@ -43,32 +43,88 @@ const ConsumerDetailModal = ({visible, group, address, 
onCancel}) => {
         fetchData();
     }, [visible, group, address]);
 
+    // Format timestamp to readable date
+    const formatTimestamp = (timestamp) => {
+        if (!timestamp || timestamp === 0) return '-';
+        return new Date(timestamp).toLocaleString();
+    };
+
+    // Group data by topic for better organization
+    const groupByTopic = (data) => {
+        const grouped = {};
+        data.forEach(item => {
+            if (!grouped[item.topic]) {
+                grouped[item.topic] = [];
+            }
+            grouped[item.topic].push(item);
+        });
+        return grouped;
+    };
+
+    const groupedDetails = groupByTopic(details);
+
     const queueColumns = [
-        {title: 'Broker', dataIndex: 'brokerName'},
-        {title: 'Queue', dataIndex: 'queueId'},
-        {title: 'BrokerOffset', dataIndex: 'brokerOffset'},
-        {title: 'ConsumerOffset', dataIndex: 'consumerOffset'},
-        {title: 'DiffTotal', dataIndex: 'diffTotal'},
-        {title: 'LastTimestamp', dataIndex: 'lastTimestamp'},
+        {title: 'Broker', dataIndex: 'brokerName', width: 120},
+        {title: 'Queue ID', dataIndex: 'queueId', width: 100},
+        {title: 'Broker Offset', dataIndex: 'brokerOffset', width: 120},
+        {title: 'Consumer Offset', dataIndex: 'consumerOffset', width: 120},
+        {
+            title: 'Lag (Diff)', dataIndex: 'diffTotal', width: 100,
+            render: (diff) => (
+                <span style={{color: diff > 0 ? '#f5222d' : '#52c41a'}}>
+                    {diff}
+                </span>
+            )
+        },
+        {title: 'Client Info', dataIndex: 'clientInfo', width: 200},
+        {
+            title: 'Last Consume Time', dataIndex: 'lastTimestamp', width: 180,
+            render: (timestamp) => formatTimestamp(timestamp)
+        },
     ];
 
     return (
         <Modal
-            title={`[${group}]${t.CONSUME_DETAIL}`}
+            title={
+                <span>Consumer Details - Group: <strong>{group}</strong> | 
Address: <strong>{address}</strong></span>}
             visible={visible}
             onCancel={onCancel}
             footer={null}
-            width={1200}
+            width={1400}
+            style={{top: 20}}
         >
             <Spin spinning={loading}>
-                {details.map((consumeDetail, index) => (
-                    <div key={index}>
-                        <Table
-                            columns={queueColumns}
-                            dataSource={consumeDetail.queueStatInfoList}
-                            rowKey="queueId"
-                            pagination={false}
-                        />
+                {Object.entries(groupedDetails).map(([topic, topicDetails]) => 
(
+                    <div key={topic} style={{marginBottom: 24}}>
+                        <div style={{
+                            background: '#f0f0f0',
+                            padding: '8px 16px',
+                            marginBottom: 8,
+                            borderRadius: 4,
+                            display: 'flex',
+                            justifyContent: 'space-between',
+                            alignItems: 'center'
+                        }}>
+                            <h3 style={{margin: 0}}>Topic: 
<strong>{topic}</strong></h3>
+                            <div>
+                                <span style={{marginRight: 16}}>Total Lag: 
<strong>{topicDetails[0].diffTotal}</strong></span>
+                                <span>Last Consume Time: 
<strong>{formatTimestamp(topicDetails[0].lastTimestamp)}</strong></span>
+                            </div>
+                        </div>
+
+                        {topicDetails.map((detail, index) => (
+                            <div key={index} style={{marginBottom: 16}}>
+                                <Table
+                                    columns={queueColumns}
+                                    dataSource={detail.queueStatInfoList}
+                                    rowKey={(record) => 
`${record.brokerName}-${record.queueId}`}
+                                    pagination={false}
+                                    size="small"
+                                    bordered
+                                    scroll={{x: 'max-content'}}
+                                />
+                            </div>
+                        ))}
                     </div>
                 ))}
             </Spin>
diff --git a/frontend-new/src/i18n/index.js b/frontend-new/src/i18n/index.js
index 0e6bce1..1486991 100644
--- a/frontend-new/src/i18n/index.js
+++ b/frontend-new/src/i18n/index.js
@@ -290,6 +290,23 @@ export const translations = {
         "USERNAME_PLACEHOLDER": "用户名",
         "PASSWORD_REQUIRED": "密码为必填项",
         "PASSWORD_PLACEHOLDER": "密码",
+        "PLEASE_INPUT_NAME":"请输入名称",
+        "PLEASE_SELECT_CLUSTER": "请选择集群",
+        "CLIENT_INFORMATION": "客户端信息",
+        "CONSUME_TYPE": "消费类型",
+        "MESSAGE_MODEL": "消息模型",
+        "CONSUME_FROM_WHERE": "从何处消费",
+        "CLIENT_CONNECTIONS": "客户端连接",
+        "CLIENT_SUBSCRIPTIONS": "客户端订阅",
+        "CONNECTION_OVERVIEW": "连接概览",
+        "CLIENTID": "客户端 ID",
+        "CLIENTADDR": "客户端地址",
+        "LANGUAGE": "语言",
+        "SUBSCRIPTION_EXPRESSION": "订阅表达式",
+        "EXPRESSION_TYPE": "表达式类型",
+        "SUB_VERSION": "订阅版本",
+        "CODE_SET": "代码集",
+        "TAGS_SET": "标签集"
     },
     en: {
         "DEFAULT": "Default",
@@ -558,6 +575,24 @@ export const translations = {
         "USERNAME_PLACEHOLDER": "Username placeholder",
         "PASSWORD_REQUIRED": "Password is required",
         "PASSWORD_PLACEHOLDER": "Password placeholder",
+        "PLEASE_INPUT_NAME": "Please input name",
+        "PLEASE_SELECT_CLUSTER": "Please select cluster",
+        "SUBSCRIPTION": "Subscription",
+        "CLIENT_INFORMATION": "Client Information",
+        "CONSUME_TYPE": "Consume Type",
+        "MESSAGE_MODEL": "Message Model",
+        "CONSUME_FROM_WHERE": "Consume From Where",
+        "CLIENT_CONNECTIONS": "Client Connections",
+        "CLIENT_SUBSCRIPTIONS": "Client Subscriptions",
+        "CONNECTION_OVERVIEW": "Connection Overview",
+        "CLIENTID": "Client ID",
+        "CLIENTADDR": "Client Address",
+        "LANGUAGE": "Language",
+        "SUBSCRIPTION_EXPRESSION": "Subscription Expression",
+        "EXPRESSION_TYPE": "Expression Type",
+        "SUB_VERSION": "Sub Version",
+        "CODE_SET": "Code Set",
+        "TAGS_SET": "Tags Set"
 
 
     }
diff --git a/frontend-new/src/pages/Acl/acl.jsx 
b/frontend-new/src/pages/Acl/acl.jsx
index aeab68d..c3652eb 100644
--- a/frontend-new/src/pages/Acl/acl.jsx
+++ b/frontend-new/src/pages/Acl/acl.jsx
@@ -15,32 +15,16 @@
  * limitations under the License.
  */
 
-import React, { useState, useEffect } from 'react';
-import {
-    Table,
-    Button,
-    Input,
-    Tabs,
-    Modal,
-    Form,
-    message,
-    Space,
-    Tag,
-    Popconfirm,
-    Select
-} from 'antd';
-import {
-    EditOutlined,
-    DeleteOutlined,
-    EyeOutlined,
-    EyeInvisibleOutlined
-} from '@ant-design/icons';
+import React, {useEffect, useState} from 'react';
+import {Button, Form, Input, message, Modal, Popconfirm, Select, Space, Table, 
Tabs, Tag} from 'antd';
+import {DeleteOutlined, EditOutlined, EyeInvisibleOutlined, EyeOutlined} from 
'@ant-design/icons';
 import {remoteApi} from "../../api/remoteApi/remoteApi";
 import ResourceInput from '../../components/acl/ResourceInput';
 import SubjectInput from "../../components/acl/SubjectInput";
 import {useLanguage} from "../../i18n/LanguageContext";
-const { TabPane } = Tabs;
-const { Search } = Input;
+
+const {TabPane} = Tabs;
+const {Search} = Input;
 
 const Acl = () => {
     const [activeTab, setActiveTab] = useState('users');
@@ -84,17 +68,19 @@ const Acl = () => {
     // State for the address of the selected broker
     const [brokerAddress, setBrokerAddress] = useState(undefined);
 
+    const [searchValue, setSearchValue] = useState('');
+
     // --- Data Fetching and Initial Setup ---
     useEffect(() => {
         const fetchData = async () => {
             const clusterResponse = await remoteApi.getClusterList();
             if (clusterResponse.status === 0 && clusterResponse.data) {
-                const { clusterInfo } = clusterResponse.data;
+                const {clusterInfo} = clusterResponse.data;
                 setClusterData(clusterInfo); // Store the entire clusterInfo
 
                 // Populate cluster names for the first dropdown
                 const clusterNames = Object.keys(clusterInfo?.clusterAddrTable 
|| {});
-                setClusterNamesOptions(clusterNames.map(name => ({ label: 
name, value: name })));
+                setClusterNamesOptions(clusterNames.map(name => ({label: name, 
value: name})));
 
                 // Set initial selections if clusters are available
                 if (clusterNames.length > 0) {
@@ -119,15 +105,15 @@ const Acl = () => {
                 console.error('Failed to fetch cluster list:', 
clusterResponse.errMsg);
             }
         };
-        if(!clusterData){
-            fetchData();
+        if (!clusterData) {
+            setLoading(true);
+            fetchData().finally(() => setLoading(false));
         }
-        if(brokerAddress){
-            // Call fetchUsers or fetchAcls based on activeTab initially
+        if (brokerAddress) {
             if (activeTab === 'users') {
-                fetchUsers();
+                fetchUsers().finally(() => setLoading(false));
             } else {
-                fetchAcls();
+                fetchAcls().finally(() => setLoading(false));
             }
         }
 
@@ -135,7 +121,6 @@ const Acl = () => {
 
     useEffect(() => {
         const userPermission = localStorage.getItem('userrole');
-        console.log(userPermission);
         if (userPermission == 2) {
             setWriteOperationEnabled(false);
         } else {
@@ -150,7 +135,7 @@ const Acl = () => {
             return;
         }
         const brokersInCluster = info.clusterAddrTable[clusterName] || [];
-        setBrokerNamesOptions(brokersInCluster.map(broker => ({ label: broker, 
value: broker })));
+        setBrokerNamesOptions(brokersInCluster.map(broker => ({label: broker, 
value: broker})));
     };
 
     // --- Event Handlers ---
@@ -174,12 +159,6 @@ const Acl = () => {
         }
     };
 
-    // --- Log selected values for debugging (optional) ---
-    useEffect(() => {
-        console.log('Selected Cluster:', selectedCluster);
-        console.log('Selected Broker:', selectedBroker);
-        console.log('Broker Address:', brokerAddress);
-    }, [selectedCluster, selectedBroker, brokerAddress]);
     const handleIpChange = value => {
         // 过滤掉重复的IP地址
         const uniqueIps = Array.from(new Set(value));
@@ -197,7 +176,7 @@ const Acl = () => {
         }
         const invalidIps = value.filter(ip => !ipRegex.test(ip));
         if (invalidIps.length > 0) {
-            return Promise.reject(t.INVALID_IP_ADDRESSES +"ips:" + 
invalidIps.join(', '));
+            return Promise.reject(t.INVALID_IP_ADDRESSES + "ips:" + 
invalidIps.join(', '));
         }
         return Promise.resolve();
     };
@@ -206,7 +185,7 @@ const Acl = () => {
     const fetchUsers = async () => {
         setLoading(true);
         try {
-            const result = await remoteApi.listUsers(brokerAddress);
+            const result = await remoteApi.listUsers(selectedBroker, 
selectedCluster);
             if (result && result.status === 0 && result.data) {
                 const formattedUsers = result.data.map(user => ({
                     ...user,
@@ -215,7 +194,7 @@ const Acl = () => {
                 }));
                 setUserListData(formattedUsers);
             } else {
-                messageApi.error(t.GET_USERS_FAILED+result?.errMsg);
+                messageApi.error(t.GET_USERS_FAILED + result?.errMsg);
             }
         } catch (error) {
             console.error("Failed to fetch users:", error);
@@ -225,10 +204,10 @@ const Acl = () => {
         }
     };
 
-    const fetchAcls = async (value) => {
+    const fetchAcls = async () => {
         setLoading(true);
         try {
-            const result = await remoteApi.listAcls(brokerAddress, value);
+            const result = await remoteApi.listAcls(selectedBroker, 
searchValue, selectedCluster);
             if (result && result.status === 0) {
                 const formattedAcls = [];
 
@@ -245,7 +224,6 @@ const Acl = () => {
                                         const resources = 
Array.isArray(entry.resource) ? entry.resource : (entry.resource ? 
[entry.resource] : []);
 
                                         resources.forEach((singleResource, 
resourceIndex) => {
-                                            console.log(singleResource)
                                             formattedAcls.push({
                                                 key: 
`acl-${aclIndex}-policy-${policyIndex}-entry-${entryIndex}-resource-${singleResource}`,
                                                 subject: subject,
@@ -301,10 +279,10 @@ const Acl = () => {
     const handleDeleteUser = async (username) => {
         setLoading(true);
         try {
-            const result = await remoteApi.deleteUser(brokerAddress, username);
+            const result = await remoteApi.deleteUser(selectedBroker, 
username, selectedCluster);
             if (result.status === 0) {
                 messageApi.success(t.USER_DELETE_SUCCESS);
-                fetchUsers(brokerAddress);
+                fetchUsers();
             } else {
                 messageApi.error(t.USER_DELETE_FAILED + result.errMsg);
             }
@@ -330,14 +308,14 @@ const Acl = () => {
             };
 
             if (currentUser) {
-                result = await remoteApi.updateUser(brokerAddress, 
userInfoParam);
+                result = await remoteApi.updateUser(selectedBroker, 
userInfoParam, selectedCluster);
                 if (result.status === 0) {
                     messageApi.success(t.USER_UPDATE_SUCCESS);
                 } else {
                     messageApi.error(result.errMsg);
                 }
             } else {
-                result = await remoteApi.createUser(brokerAddress, 
userInfoParam);
+                result = await remoteApi.createUser(selectedBroker, 
userInfoParam, selectedCluster);
                 if (result.status === 0) {
                     messageApi.success(t.USER_CREATE_SUCCESS);
                 } else {
@@ -379,12 +357,12 @@ const Acl = () => {
     const handleDeleteAcl = async (subject, resource) => {
         setLoading(true);
         try {
-            const result = await remoteApi.deleteAcl(brokerAddress, subject, 
resource);
+            const result = await remoteApi.deleteAcl(selectedBroker, subject, 
resource, selectedCluster);
             if (result.status === 0) {
                 messageApi.success(t.ACL_DELETE_SUCCESS);
                 fetchAcls();
             } else {
-                messageApi.error(t.ACL_DELETE_FAILED+result.errMsg);
+                messageApi.error(t.ACL_DELETE_FAILED + result.errMsg);
             }
         } catch (error) {
             console.error("Failed to delete ACL:", error);
@@ -415,24 +393,23 @@ const Acl = () => {
             ];
 
             if (isUpdate) { // This condition seems reversed for update/create 
based on the current logic.
-                result = await remoteApi.updateAcl(brokerAddress, 
values.subject, policiesParam);
+                result = await remoteApi.updateAcl(selectedBroker, 
values.subject, policiesParam, selectedCluster);
                 if (result.status === 0) {
                     messageApi.success(t.ACL_UPDATE_SUCCESS);
                     setIsAclModalVisible(false);
-                    fetchAcls(brokerAddress);
+                    fetchAcls();
                 } else {
-                    messageApi.error(t.ACL_UPDATE_FAILED+result.errMsg);
+                    messageApi.error(t.ACL_UPDATE_FAILED + result.errMsg);
                 }
                 setIsUpdate(false)
             } else {
-                result = await remoteApi.createAcl(brokerAddress, 
values.subject, policiesParam);
-                console.log(result)
+                result = await remoteApi.createAcl(selectedBroker, 
values.subject, policiesParam, selectedCluster);
                 if (result.status === 0) {
                     messageApi.success(t.ACL_CREATE_SUCCESS);
                     setIsAclModalVisible(false);
-                    fetchAcls(brokerAddress);
+                    fetchAcls();
                 } else {
-                    messageApi.error(t.ACL_CREATE_FAILED+result.errMsg);
+                    messageApi.error(t.ACL_CREATE_FAILED + result.errMsg);
                 }
             }
 
@@ -444,6 +421,10 @@ const Acl = () => {
         }
     };
 
+    const handleInputChange = (e) => {
+        setSearchValue(e.target.value);
+    };
+
 // --- Search Functionality ---
 
     const handleSearch = (value) => {
@@ -459,7 +440,7 @@ const Acl = () => {
                 setUserListData(filteredData);
             }
         } else {
-            fetchAcls(value);
+            fetchAcls();
         }
     };
 
@@ -480,9 +461,9 @@ const Acl = () => {
                 {showPassword ? text : '********'}
                     <Button
                         type="link"
-                        icon={showPassword ? <EyeInvisibleOutlined /> : 
<EyeOutlined />}
+                        icon={showPassword ? <EyeInvisibleOutlined/> : 
<EyeOutlined/>}
                         onClick={() => setShowPassword(!showPassword)}
-                        style={{ marginLeft: 8 }}
+                        style={{marginLeft: 8}}
                     >
                     {showPassword ? t.HIDE : t.VIEW}
                 </Button>
@@ -508,14 +489,14 @@ const Acl = () => {
             render: (_, record) => (
                 writeOperationEnabled ? (
                     <Space size="middle">
-                        <Button icon={<EditOutlined />} onClick={() => 
handleEditUser(record)}>{t.MODIFY}</Button>
+                        <Button icon={<EditOutlined/>} onClick={() => 
handleEditUser(record)}>{t.MODIFY}</Button>
                         <Popconfirm
                             title={t.CONFIRM_DELETE_USER}
                             onConfirm={() => handleDeleteUser(record.username)}
                             okText={t.YES}
                             cancelText={t.NO}
                         >
-                            <Button icon={<DeleteOutlined />} 
danger>{t.DELETE}</Button>
+                            <Button icon={<DeleteOutlined/>} 
danger>{t.DELETE}</Button>
                         </Popconfirm>
                     </Space>
                 ) : null
@@ -567,14 +548,14 @@ const Acl = () => {
             render: (_, record) => (
                 writeOperationEnabled ? (
                     <Space size="middle">
-                        <Button icon={<EditOutlined />} onClick={() => 
handleEditAcl(record)}>{t.MODIFY}</Button>
+                        <Button icon={<EditOutlined/>} onClick={() => 
handleEditAcl(record)}>{t.MODIFY}</Button>
                         <Popconfirm
                             title={t.CONFIRM_DELETE_ACL}
                             onConfirm={() => handleDeleteAcl(record.subject, 
record.resource)}
                             okText={t.YES}
                             cancelText={t.NO}
                         >
-                            <Button icon={<DeleteOutlined />} 
danger>{t.DELETE}</Button>
+                            <Button icon={<DeleteOutlined/>} 
danger>{t.DELETE}</Button>
                         </Popconfirm>
                     </Space>
                 ) : null
@@ -582,233 +563,236 @@ const Acl = () => {
         },
     ];
 
-return (
-    <>
-        {msgContextHolder}
-        <div style={{padding: 24}}>
-            <h2>{t.ACL_MANAGEMENT}</h2>
-
-            <div style={{ marginBottom: 16, display: 'flex', gap: 16 }}>
-                <Form.Item label={t.PLEASE_SELECT_CLUSTER} style={{ 
marginBottom: 0 }}>
-                    <Select
-                        placeholder={t.PLEASE_SELECT_CLUSTER}
-                        style={{ width: 200 }}
-                        onChange={handleClusterChange}
-                        value={selectedCluster}
-                        options={clusterNamesOptions}
-                    />
-                </Form.Item>
-                <Form.Item label={t.PLEASE_SELECT_BROKER} style={{ 
marginBottom: 0 }}>
-                    <Select
-                        placeholder={t.PLEASE_SELECT_BROKER}
-                        style={{ width: 200 }}
-                        onChange={handleBrokerChange}
-                        value={selectedBroker}
-                        options={brokerNamesOptions} // Now dynamically updated
-                        disabled={!selectedCluster} // Disable broker 
selection if no cluster is chosen
-                    />
-                </Form.Item>
-                <Button type="primary" onClick={activeTab === 'users' ? 
fetchUsers : fetchAcls}>
-                    {t.CONFIRM}
-                </Button>
-            </div>
-
-            <Tabs activeKey={activeTab} onChange={setActiveTab}>
-                <TabPane tab={t.ACL_USERS} key="users"/>
-                <TabPane tab={t.ACL_PERMISSIONS} key="acls"/>
-            </Tabs>
+    return (
+        <>
+            {msgContextHolder}
+            <div style={{padding: 24}}>
+                <h2>{t.ACL_MANAGEMENT}</h2>
 
-            <div style={{marginBottom: 16, display: 'flex', justifyContent: 
'space-between'}}>
-                <Button type="primary" onClick={activeTab === 'users' ? 
handleAddUser : handleAddAcl}>
-                    {activeTab === 'users' ? t.ADD_USER : t.ADD_ACL_PERMISSION}
-                </Button>
-                <Search
-                    placeholder={t.SEARCH_PLACEHOLDER}
-                    allowClear
-                    onSearch={handleSearch}
-                    style={{width: 300}}
-                />
-            </div>
-
-            {activeTab === 'users' && (
-                <Table
-                    columns={userColumns}
-                    dataSource={userListData}
-                    loading={loading}
-                    pagination={{pageSize: 10}}
-                    rowKey="username"
-                />
-            )}
-
-            {activeTab === 'acls' && (
-                <Table
-                    columns={aclColumns}
-                    dataSource={aclListData}
-                    loading={loading}
-                    pagination={{pageSize: 10}}
-                    rowKey="key"
-                />
-            )}
-
-            {/* User Management Modal */}
-            <Modal
-                title={currentUser ? t.EDIT_USER : t.ADD_USER}
-                visible={isUserModalVisible}
-                onOk={handleUserModalOk}
-                onCancel={() => setIsUserModalVisible(false)}
-                confirmLoading={loading}
-                footer={[
-                    <Button key="cancel" onClick={() => 
setIsUserModalVisible(false)}>
-                        {t.CANCEL}
-                    </Button>,
-                    <Button key="submit" type="primary" 
onClick={handleUserModalOk} loading={loading}>
-                        {t.CONFIRM}
-                    </Button>,
-                ]}
-            >
-                <Form
-                    form={userForm}
-                    layout="vertical"
-                    name="user_form"
-                    initialValues={{userStatus: 'enable'}}
-                >
-                    <Form.Item
-                        name="username"
-                        label={t.USERNAME}
-                        rules={[{required: true, message: 
t.PLEASE_ENTER_USERNAME}]}
-                    >
-                        <Input disabled={!!currentUser}/>
-                    </Form.Item>
-                    <Form.Item
-                        name="password"
-                        label={t.PASSWORD}
-                        rules={[{required: !currentUser, message: 
t.PLEASE_ENTER_PASSWORD}]}
-                    >
-                        <Input.Password
-                            placeholder={t.PASSWORD}
-                            iconRender={visible => (visible ? <EyeOutlined/> : 
<EyeInvisibleOutlined/>)}
+                <div style={{marginBottom: 16, display: 'flex', gap: 16}}>
+                    <Form.Item label={t.PLEASE_SELECT_CLUSTER} 
style={{marginBottom: 0}}>
+                        <Select
+                            placeholder={t.PLEASE_SELECT_CLUSTER}
+                            style={{width: 200}}
+                            onChange={handleClusterChange}
+                            value={selectedCluster}
+                            options={clusterNamesOptions}
                         />
                     </Form.Item>
-                    <Form.Item
-                        name="userType"
-                        label={t.USER_TYPE}
-                        rules={[{required: true, message: 
t.PLEASE_SELECT_USER_TYPE}]}
-                    >
-                        <Select mode="single" placeholder="Super, Normal" 
style={{width: '100%'}}>
-                            <Select.Option value="Super">Super</Select.Option>
-                            <Select.Option 
value="Normal">Normal</Select.Option>
-                        </Select>
-                    </Form.Item>
-                    <Form.Item
-                        name="userStatus"
-                        label={t.USER_STATUS}
-                        rules={[{required: true, message: 
t.PLEASE_SELECT_USER_STATUS}]}
-                    >
-                        <Select mode="single" placeholder="enable, disable" 
style={{width: '100%'}}>
-                            <Select.Option 
value="enable">enable</Select.Option>
-                            <Select.Option 
value="disable">disable</Select.Option>
-                        </Select>
+                    <Form.Item label={t.PLEASE_SELECT_BROKER} 
style={{marginBottom: 0}}>
+                        <Select
+                            placeholder={t.PLEASE_SELECT_BROKER}
+                            style={{width: 200}}
+                            onChange={handleBrokerChange}
+                            value={selectedBroker}
+                            options={brokerNamesOptions}
+                            disabled={!selectedCluster}
+                            allowClear
+                        />
                     </Form.Item>
-                </Form>
-            </Modal>
-
-            {/* ACL Permission Management Modal */}
-            <Modal
-                title={currentAcl ? t.EDIT_ACL_PERMISSION : 
t.ADD_ACL_PERMISSION}
-                visible={isAclModalVisible}
-                onOk={handleAclModalOk}
-                onCancel={() => setIsAclModalVisible(false)}
-                confirmLoading={loading}
-            >
-                <Form
-                    form={aclForm}
-                    layout="vertical"
-                    name="acl_form"
+                    <Button type="primary" onClick={activeTab === 'users' ? 
fetchUsers : fetchAcls}>
+                        {t.CONFIRM}
+                    </Button>
+                </div>
+
+                <Tabs activeKey={activeTab} onChange={setActiveTab}>
+                    <TabPane tab={t.ACL_USERS} key="users"/>
+                    <TabPane tab={t.ACL_PERMISSIONS} key="acls"/>
+                </Tabs>
+
+                <div style={{marginBottom: 16, display: 'flex', 
justifyContent: 'space-between'}}>
+                    <Button type="primary" onClick={activeTab === 'users' ? 
handleAddUser : handleAddAcl}>
+                        {activeTab === 'users' ? t.ADD_USER : 
t.ADD_ACL_PERMISSION}
+                    </Button>
+                    <Search
+                        placeholder={t.SEARCH_PLACEHOLDER}
+                        allowClear
+                        onSearch={handleSearch}
+                        value={searchValue}
+                        onChange={handleInputChange}
+                        style={{width: 300}}
+                    />
+                </div>
+
+                {activeTab === 'users' && (
+                    <Table
+                        columns={userColumns}
+                        dataSource={userListData}
+                        loading={loading}
+                        pagination={{pageSize: 10}}
+                        rowKey="username"
+                    />
+                )}
+
+                {activeTab === 'acls' && (
+                    <Table
+                        columns={aclColumns}
+                        dataSource={aclListData}
+                        loading={loading}
+                        pagination={{pageSize: 10}}
+                        rowKey="key"
+                    />
+                )}
+
+                {/* User Management Modal */}
+                <Modal
+                    title={currentUser ? t.EDIT_USER : t.ADD_USER}
+                    visible={isUserModalVisible}
+                    onOk={handleUserModalOk}
+                    onCancel={() => setIsUserModalVisible(false)}
+                    confirmLoading={loading}
+                    footer={[
+                        <Button key="cancel" onClick={() => 
setIsUserModalVisible(false)}>
+                            {t.CANCEL}
+                        </Button>,
+                        <Button key="submit" type="primary" 
onClick={handleUserModalOk} loading={loading}>
+                            {t.CONFIRM}
+                        </Button>,
+                    ]}
                 >
-                    <Form.Item
-                        name="subject"
-                        label={t.SUBJECT_LABEL}
-                        rules={[{required: true, message: 
t.PLEASE_ENTER_SUBJECT}]}
-                    >
-                        <SubjectInput disabled={!!currentAcl}/>
-                    </Form.Item>
-
-                    <Form.Item
-                        name="policyType"
-                        label={t.POLICY_TYPE}
-                        rules={[{required: true, message: 
t.PLEASE_ENTER_POLICY_TYPE}]}
+                    <Form
+                        form={userForm}
+                        layout="vertical"
+                        name="user_form"
+                        initialValues={{userStatus: 'enable'}}
                     >
-                        <Select mode="single" disabled={isUpdate} 
placeholder="policyType" style={{width: '100%'}}>
-                            <Select.Option 
value="Custom">Custom</Select.Option>
-                            <Select.Option 
value="Default">Default</Select.Option>
-                        </Select>
-                    </Form.Item>
-
-                    <Form.Item
-                        name="resource"
-                        label={t.RESOURCE}
-                        rules={[{required: true, message: 
t.PLEASE_ADD_RESOURCE}]}
+                        <Form.Item
+                            name="username"
+                            label={t.USERNAME}
+                            rules={[{required: true, message: 
t.PLEASE_ENTER_USERNAME}]}
+                        >
+                            <Input disabled={!!currentUser}/>
+                        </Form.Item>
+                        <Form.Item
+                            name="password"
+                            label={t.PASSWORD}
+                            rules={[{required: !currentUser, message: 
t.PLEASE_ENTER_PASSWORD}]}
+                        >
+                            <Input.Password
+                                placeholder={t.PASSWORD}
+                                iconRender={visible => (visible ? 
<EyeOutlined/> : <EyeInvisibleOutlined/>)}
+                            />
+                        </Form.Item>
+                        <Form.Item
+                            name="userType"
+                            label={t.USER_TYPE}
+                            rules={[{required: true, message: 
t.PLEASE_SELECT_USER_TYPE}]}
+                        >
+                            <Select mode="single" placeholder="Super, Normal" 
style={{width: '100%'}}>
+                                <Select.Option 
value="Super">Super</Select.Option>
+                                <Select.Option 
value="Normal">Normal</Select.Option>
+                            </Select>
+                        </Form.Item>
+                        <Form.Item
+                            name="userStatus"
+                            label={t.USER_STATUS}
+                            rules={[{required: true, message: 
t.PLEASE_SELECT_USER_STATUS}]}
+                        >
+                            <Select mode="single" placeholder="enable, 
disable" style={{width: '100%'}}>
+                                <Select.Option 
value="enable">enable</Select.Option>
+                                <Select.Option 
value="disable">disable</Select.Option>
+                            </Select>
+                        </Form.Item>
+                    </Form>
+                </Modal>
+
+                {/* ACL Permission Management Modal */}
+                <Modal
+                    title={currentAcl ? t.EDIT_ACL_PERMISSION : 
t.ADD_ACL_PERMISSION}
+                    visible={isAclModalVisible}
+                    onOk={handleAclModalOk}
+                    onCancel={() => setIsAclModalVisible(false)}
+                    confirmLoading={loading}
+                >
+                    <Form
+                        form={aclForm}
+                        layout="vertical"
+                        name="acl_form"
                     >
-                        {isUpdate ? (
-                            <Input disabled={isUpdate}/>
-                        ) : (
-                            <ResourceInput/>
-                        )}
-                    </Form.Item>
+                        <Form.Item
+                            name="subject"
+                            label={t.SUBJECT_LABEL}
+                            rules={[{required: true, message: 
t.PLEASE_ENTER_SUBJECT}]}
+                        >
+                            <SubjectInput disabled={!!currentAcl} t={t}/>
+                        </Form.Item>
 
-                    <Form.Item
-                        name="actions"
-                        label={t.OPERATION_TYPE}
-                    >
-                        <Select mode="multiple" placeholder="action" 
style={{width: '100%'}}>
-                            <Select.Option value="All">All</Select.Option>
-                            <Select.Option value="Pub">Pub</Select.Option>
-                            <Select.Option value="Sub">Sub</Select.Option>
-                            <Select.Option 
value="Create">Create</Select.Option>
-                            <Select.Option 
value="Update">Update</Select.Option>
-                            <Select.Option 
value="Delete">Delete</Select.Option>
-                            <Select.Option value="Get">Get</Select.Option>
-                            <Select.Option value="List">List</Select.Option>
-                        </Select>
-                    </Form.Item>
-                    <Form.Item
-                        name="sourceIps"
-                        label={t.SOURCE_IP}
-                        rules={[
-                            {
-                                validator: validateIp,
-                            },
-                        ]}
-                    >
-                        <Select
-                            mode="tags"
-                            style={{width: '100%'}}
-                            placeholder={t.ENTER_IP_HINT}
-                            onChange={handleIpChange}
-                            onDeselect={handleIpDeselect}
-                            value={ips}
-                            tokenSeparators={[',', ' ']}
+                        <Form.Item
+                            name="policyType"
+                            label={t.POLICY_TYPE}
+                            rules={[{required: true, message: 
t.PLEASE_ENTER_POLICY_TYPE}]}
                         >
-                            <Select.Option 
value="192.168.1.1">192.168.1.1</Select.Option>
-                            <Select.Option 
value="0.0.0.0">0.0.0.0</Select.Option>
-                            <Select.Option 
value="127.0.0.1">127.0.0.1</Select.Option>
-                        </Select>
-                    </Form.Item>
-                    <Form.Item
-                        name="decision"
-                        label={t.DECISION}
-                        rules={[{required: true, message: 
t.PLEASE_ENTER_DECISION}]}
-                    >
-                        <Select mode="single" placeholder="Allow, Deny" 
style={{width: '100%'}}>
-                            <Select.Option value="Allow">Allow</Select.Option>
-                            <Select.Option value="Deny">Deny</Select.Option>
-                        </Select>
-                    </Form.Item>
-                </Form>
-            </Modal>
-        </div>
-    </>
-);
+                            <Select mode="single" disabled={isUpdate} 
placeholder="policyType" style={{width: '100%'}}>
+                                <Select.Option 
value="Custom">Custom</Select.Option>
+                                <Select.Option 
value="Default">Default</Select.Option>
+                            </Select>
+                        </Form.Item>
+
+                        <Form.Item
+                            name="resource"
+                            label={t.RESOURCE}
+                            rules={[{required: true, message: 
t.PLEASE_ADD_RESOURCE}]}
+                        >
+                            {isUpdate ? (
+                                <Input disabled={isUpdate}/>
+                            ) : (
+                                <ResourceInput/>
+                            )}
+                        </Form.Item>
+
+                        <Form.Item
+                            name="actions"
+                            label={t.OPERATION_TYPE}
+                        >
+                            <Select mode="multiple" placeholder="action" 
style={{width: '100%'}}>
+                                <Select.Option value="All">All</Select.Option>
+                                <Select.Option value="Pub">Pub</Select.Option>
+                                <Select.Option value="Sub">Sub</Select.Option>
+                                <Select.Option 
value="Create">Create</Select.Option>
+                                <Select.Option 
value="Update">Update</Select.Option>
+                                <Select.Option 
value="Delete">Delete</Select.Option>
+                                <Select.Option value="Get">Get</Select.Option>
+                                <Select.Option 
value="List">List</Select.Option>
+                            </Select>
+                        </Form.Item>
+                        <Form.Item
+                            name="sourceIps"
+                            label={t.SOURCE_IP}
+                            rules={[
+                                {
+                                    validator: validateIp,
+                                },
+                            ]}
+                        >
+                            <Select
+                                mode="tags"
+                                style={{width: '100%'}}
+                                placeholder={t.ENTER_IP_HINT}
+                                onChange={handleIpChange}
+                                onDeselect={handleIpDeselect}
+                                value={ips}
+                                tokenSeparators={[',', ' ']}
+                            >
+                                <Select.Option 
value="192.168.1.1">192.168.1.1</Select.Option>
+                                <Select.Option 
value="0.0.0.0">0.0.0.0</Select.Option>
+                                <Select.Option 
value="127.0.0.1">127.0.0.1</Select.Option>
+                            </Select>
+                        </Form.Item>
+                        <Form.Item
+                            name="decision"
+                            label={t.DECISION}
+                            rules={[{required: true, message: 
t.PLEASE_ENTER_DECISION}]}
+                        >
+                            <Select mode="single" placeholder="Allow, Deny" 
style={{width: '100%'}}>
+                                <Select.Option 
value="Allow">Allow</Select.Option>
+                                <Select.Option 
value="Deny">Deny</Select.Option>
+                            </Select>
+                        </Form.Item>
+                    </Form>
+                </Modal>
+            </div>
+        </>
+    );
 }
 
 export default Acl;
diff --git a/frontend-new/src/pages/Consumer/consumer.jsx 
b/frontend-new/src/pages/Consumer/consumer.jsx
index fb3da7b..650bafa 100644
--- a/frontend-new/src/pages/Consumer/consumer.jsx
+++ b/frontend-new/src/pages/Consumer/consumer.jsx
@@ -66,7 +66,7 @@ const ConsumerGroupList = () => {
         }
     });
 
-    const [proxyOptions ,setProxyOptions]= useState([]);
+    const [proxyOptions, setProxyOptions] = useState([]);
     const [paginationConf, setPaginationConf] = useState({
         current: 1,
         pageSize: 10,
@@ -82,9 +82,9 @@ const ConsumerGroupList = () => {
         setLoading(true);
         try {
             var response;
-            if(!proxyEnabled){
+            if (!proxyEnabled) {
                 response = await remoteApi.queryConsumerGroupList(false);
-            }else{
+            } else {
                 response = await remoteApi.queryConsumerGroupList(false, 
selectedProxy);
             }
             if (response.status === 0) {
@@ -98,12 +98,12 @@ const ConsumerGroupList = () => {
                 messageApi.error({title: t.ERROR, content: response.errMsg});
             }
         } catch (error) {
-            messageApi.error({title: t.ERROR, content: 
t.FAILED_TO_FETCH_DATA});
             console.error("Error loading consumer groups:", error);
+            messageApi.error({title: t.ERROR, content: 
t.FAILED_TO_FETCH_DATA});
         } finally {
             setLoading(false);
         }
-    }, [t]);
+    }, [t, proxyEnabled, selectedProxy, messageApi, setAllConsumerGroupList, 
remoteApi, setLoading]);
 
     const filterByType = (str, type, version) => {
         if (filterSystem && type === "SYSTEM") return true;
@@ -465,15 +465,21 @@ const ConsumerGroupList = () => {
         <>
             {msgContextHolder}
             {notificationContextHolder}
-            <div style={{ padding: '20px' }}>
+            <div style={{padding: '20px'}}>
                 <Spin spinning={loading} tip={t.LOADING}>
-                    <div style={{ marginBottom: '20px', display: 'flex', 
justifyContent: 'space-between', alignItems: 'center' }}>
+                    <div style={{
+                        marginBottom: '20px',
+                        display: 'flex',
+                        justifyContent: 'space-between',
+                        alignItems: 'center'
+                    }}>
                         {/* 左侧:筛选和操作按钮 */}
-                        <div style={{ display: 'flex', alignItems: 'center', 
gap: '15px', flexWrap: 'wrap' }}>
-                            <div style={{ display: 'flex', alignItems: 
'center' }}>
-                                <label style={{ marginRight: '8px', 
whiteSpace: 'nowrap' }}>{t.SUBSCRIPTION_GROUP}:</label>
+                        <div style={{display: 'flex', alignItems: 'center', 
gap: '15px', flexWrap: 'wrap'}}>
+                            <div style={{display: 'flex', alignItems: 
'center'}}>
+                                <label
+                                    style={{marginRight: '8px', whiteSpace: 
'nowrap'}}>{t.SUBSCRIPTION_GROUP}:</label>
                                 <Input
-                                    style={{ width: '200px' }}
+                                    style={{width: '200px'}}
                                     value={filterStr}
                                     onChange={(e) => 
handleFilterInputChange(e.target.value)}
                                     placeholder="输入订阅组名称"
@@ -504,10 +510,10 @@ const ConsumerGroupList = () => {
                         </div>
 
                         {/* 右侧:代理选项 */}
-                        <div style={{ display: 'flex', alignItems: 'center', 
gap: '15px' }}>
-                            <label style={{ marginRight: '8px', whiteSpace: 
'nowrap' }}>{t.SELECT_PROXY}:</label>
+                        <div style={{display: 'flex', alignItems: 'center', 
gap: '15px'}}>
+                            <label style={{marginRight: '8px', whiteSpace: 
'nowrap'}}>{t.SELECT_PROXY}:</label>
                             <Select
-                                style={{ width: '220px' }}
+                                style={{width: '220px'}}
                                 placeholder={t.SELECT_PROXY}
                                 onChange={(value) => setSelectedProxy(value)}
                                 value={selectedProxy}
@@ -515,7 +521,7 @@ const ConsumerGroupList = () => {
                                 disabled={!proxyEnabled}
                                 allowClear
                             />
-                            <label style={{ marginRight: '8px', whiteSpace: 
'nowrap' }}>{t.ENABLE_PROXY}:</label>
+                            <label style={{marginRight: '8px', whiteSpace: 
'nowrap'}}>{t.ENABLE_PROXY}:</label>
                             <Switch
                                 checked={proxyEnabled}
                                 onChange={(checked) => {
diff --git a/frontend-new/src/pages/Message/message.jsx 
b/frontend-new/src/pages/Message/message.jsx
index 323a271..912d3f6 100644
--- a/frontend-new/src/pages/Message/message.jsx
+++ b/frontend-new/src/pages/Message/message.jsx
@@ -33,10 +33,9 @@ const MessageQueryPage = () => {
     const [form] = Form.useForm();
     const [loading, setLoading] = useState(false);
 
-    // Topic 查询状态
     const [allTopicList, setAllTopicList] = useState([]);
     const [selectedTopic, setSelectedTopic] = useState(null);
-    const [timepickerBegin, setTimepickerBegin] = 
useState(moment().subtract(1, 'hour')); // 默认一小时前
+    const [timepickerBegin, setTimepickerBegin] = 
useState(moment().subtract(1, 'hour'));
     const [timepickerEnd, setTimepickerEnd] = useState(moment());
     const [messageShowList, setMessageShowList] = useState([]);
     const [paginationConf, setPaginationConf] = useState({
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/controller/AclController.java 
b/src/main/java/org/apache/rocketmq/dashboard/controller/AclController.java
index 787667a..3d7b72a 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/controller/AclController.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/controller/AclController.java
@@ -18,10 +18,10 @@
 package org.apache.rocketmq.dashboard.controller;
 
 import org.apache.rocketmq.dashboard.model.PolicyRequest;
+import org.apache.rocketmq.dashboard.model.UserInfoDto;
 import org.apache.rocketmq.dashboard.model.request.UserCreateRequest;
 import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
 import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
-import org.apache.rocketmq.remoting.protocol.body.UserInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -44,16 +44,18 @@ public class AclController {
 
     @GetMapping("/users.query")
     @ResponseBody
-    public List<UserInfo> listUsers(@RequestParam(required = false) String 
brokerAddress) {
-        return aclService.listUsers(brokerAddress);
+    public List<UserInfoDto> listUsers(@RequestParam(required = false) String 
brokerName,
+                                       @RequestParam(required = false) String 
clusterName) {
+        return aclService.listUsers(clusterName, brokerName);
     }
 
     @GetMapping("/acls.query")
     @ResponseBody
     public Object listAcls(
-            @RequestParam(required = false) String brokerAddress,
-            @RequestParam(required = false) String searchParam) {
-        return aclService.listAcls(brokerAddress, searchParam);
+            @RequestParam(required = false) String brokerName,
+            @RequestParam(required = false) String searchParam,
+            @RequestParam(required = false) String clusterName) {
+        return aclService.listAcls(clusterName, brokerName, searchParam);
     }
 
     @PostMapping("/createAcl.do")
@@ -65,30 +67,35 @@ public class AclController {
 
     @DeleteMapping("/deleteUser.do")
     @ResponseBody
-    public Object deleteUser(@RequestParam(required = false) String 
brokerAddress, @RequestParam String username) {
-        aclService.deleteUser(brokerAddress, username);
+    public Object deleteUser(@RequestParam(required = false) String brokerName,
+                             @RequestParam String username,
+                             @RequestParam(required = false) String 
clusterName) {
+        aclService.deleteUser(clusterName, brokerName, username);
         return true;
     }
 
     @RequestMapping(value = "/updateUser.do", method = RequestMethod.POST, 
produces = "application/json;charset=UTF-8")
     @ResponseBody
     public Object updateUser(@RequestBody UserUpdateRequest request) {
-        aclService.updateUser(request.getBrokerAddress(), 
request.getUserInfo());
+        aclService.updateUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
         return true;
     }
 
     @PostMapping("/createUser.do")
+    @ResponseBody
     public Object createUser(@RequestBody UserCreateRequest request) {
-        aclService.createUser(request.getBrokerAddress(), 
request.getUserInfo());
+        aclService.createUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
         return true;
     }
 
     @DeleteMapping("/deleteAcl.do")
+    @ResponseBody
     public Object deleteAcl(
-            @RequestParam(required = false) String brokerAddress,
+            @RequestParam(required = false) String brokerName,
+            @RequestParam(required = false) String clusterName,
             @RequestParam String subject,
             @RequestParam(required = false) String resource) {
-        aclService.deleteAcl(brokerAddress, subject, resource);
+        aclService.deleteAcl(clusterName, brokerName, subject, resource);
         return true;
     }
 
diff --git a/src/main/java/org/apache/rocketmq/dashboard/model/AclInfo.java 
b/src/main/java/org/apache/rocketmq/dashboard/model/AclInfo.java
new file mode 100644
index 0000000..87df246
--- /dev/null
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/AclInfo.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.dashboard.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class AclInfo {
+
+    private String subject;
+    private List<PolicyInfo> policies;
+
+    public static AclInfo of(String subject, List<String> resources, 
List<String> actions,
+                             List<String> sourceIps,
+                             String decision) {
+        AclInfo aclInfo = new AclInfo();
+        aclInfo.setSubject(subject);
+        PolicyInfo policyInfo = PolicyInfo.of(resources, actions, sourceIps, 
decision);
+        aclInfo.setPolicies(Collections.singletonList(policyInfo));
+        return aclInfo;
+    }
+
+    public static class PolicyInfo {
+
+        private String policyType;
+        private List<PolicyEntryInfo> entries;
+
+        public static PolicyInfo of(List<String> resources, List<String> 
actions,
+                                    List<String> sourceIps, String decision) {
+            PolicyInfo policyInfo = new PolicyInfo();
+            List<PolicyEntryInfo> entries = resources.stream()
+                    .map(resource -> PolicyEntryInfo.of(resource, actions, 
sourceIps, decision))
+                    .collect(Collectors.toList());
+            policyInfo.setEntries(entries);
+            return policyInfo;
+        }
+
+        public String getPolicyType() {
+            return policyType;
+        }
+
+        public void setPolicyType(String policyType) {
+            this.policyType = policyType;
+        }
+
+        public List<PolicyEntryInfo> getEntries() {
+            return entries;
+        }
+
+        public void setEntries(List<PolicyEntryInfo> entries) {
+            this.entries = entries;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PolicyInfo that = (PolicyInfo) o;
+            return Objects.equals(policyType, that.policyType) &&
+                    Objects.equals(entries, that.entries);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(policyType, entries);
+        }
+    }
+
+    public static class PolicyEntryInfo {
+        private String resource;
+        private List<String> actions;
+        private List<String> sourceIps;
+        private String decision;
+
+        public static PolicyEntryInfo of(String resource, List<String> 
actions, List<String> sourceIps,
+                                         String decision) {
+            PolicyEntryInfo policyEntryInfo = new PolicyEntryInfo();
+            policyEntryInfo.setResource(resource);
+            policyEntryInfo.setActions(actions);
+            policyEntryInfo.setSourceIps(sourceIps);
+            policyEntryInfo.setDecision(decision);
+            return policyEntryInfo;
+        }
+
+        public String getResource() {
+            return resource;
+        }
+
+        public void setResource(String resource) {
+            this.resource = resource;
+        }
+
+        public List<String> getActions() {
+            return actions;
+        }
+
+        public void setActions(List<String> actions) {
+            this.actions = actions;
+        }
+
+        public List<String> getSourceIps() {
+            return sourceIps;
+        }
+
+        public void setSourceIps(List<String> sourceIps) {
+            this.sourceIps = sourceIps;
+        }
+
+        public String getDecision() {
+            return decision;
+        }
+
+        public void setDecision(String decision) {
+            this.decision = decision;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PolicyEntryInfo that = (PolicyEntryInfo) o;
+            return Objects.equals(resource, that.resource) &&
+                    Objects.equals(actions, that.actions) &&
+                    Objects.equals(sourceIps, that.sourceIps) &&
+                    Objects.equals(decision, that.decision);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(resource, actions, sourceIps, decision);
+        }
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public List<PolicyInfo> getPolicies() {
+        return policies;
+    }
+
+    public void setPolicies(List<PolicyInfo> policies) {
+        this.policies = policies;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AclInfo aclInfo = (AclInfo) o;
+        return Objects.equals(subject, aclInfo.subject) &&
+                Objects.equals(policies, aclInfo.policies);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subject, policies);
+    }
+
+    public void copyFrom(org.apache.rocketmq.remoting.protocol.body.AclInfo 
source) {
+        this.subject = source.getSubject();
+        if (source.getPolicies() != null) {
+            List<PolicyInfo> copiedPolicies = new ArrayList<>();
+            for (org.apache.rocketmq.remoting.protocol.body.AclInfo.PolicyInfo 
policy : source.getPolicies()) {
+                PolicyInfo copiedPolicy = new PolicyInfo();
+                copiedPolicy.setPolicyType(policy.getPolicyType());
+                if (policy.getEntries() != null) {
+                    List<PolicyEntryInfo> copiedEntries = new ArrayList<>();
+                    for 
(org.apache.rocketmq.remoting.protocol.body.AclInfo.PolicyEntryInfo entry : 
policy.getEntries()) {
+                        PolicyEntryInfo copiedEntry = new PolicyEntryInfo();
+                        copiedEntry.setResource(entry.getResource());
+                        copiedEntry.setActions(new 
ArrayList<>(entry.getActions()));
+                        copiedEntry.setSourceIps(new 
ArrayList<>(entry.getSourceIps()));
+                        copiedEntry.setDecision(entry.getDecision());
+                        copiedEntries.add(copiedEntry);
+                    }
+                    copiedPolicy.setEntries(copiedEntries);
+                }
+                copiedPolicies.add(copiedPolicy);
+            }
+            this.setPolicies(copiedPolicies);
+        } else {
+            this.setPolicies(null);
+        }
+    }
+
+}
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java 
b/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java
index 43dcd0d..8eb6346 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java
@@ -25,7 +25,8 @@ import java.util.List;
 @Getter
 @Setter
 public class PolicyRequest {
-    private String brokerAddress;
+    private String clusterName;
+    private String brokerName;
     private String subject;
     private List<Policy> policies;
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java 
b/src/main/java/org/apache/rocketmq/dashboard/model/UserInfoDto.java
similarity index 57%
copy from src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java
copy to src/main/java/org/apache/rocketmq/dashboard/model/UserInfoDto.java
index 43dcd0d..c8e4074 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/model/PolicyRequest.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/model/UserInfoDto.java
@@ -17,15 +17,29 @@
 
 package org.apache.rocketmq.dashboard.model;
 
-import lombok.Getter;
-import lombok.Setter;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.rocketmq.remoting.protocol.body.UserInfo;
 
-import java.util.List;
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserInfoDto {
 
-@Getter
-@Setter
-public class PolicyRequest {
-    private String brokerAddress;
-    private String subject;
-    private List<Policy> policies;
+    private String username;
+
+    private String password;
+
+    private String userType;
+
+    private String userStatus;
+
+    public UserInfoDto setUserInfo(UserInfo userInfo) {
+        this.username = userInfo.getUsername();
+        this.password = userInfo.getPassword();
+        this.userType = userInfo.getUserType();
+        this.userStatus = userInfo.getUserStatus();
+        return this;
+    }
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/model/request/UserCreateRequest.java
 
b/src/main/java/org/apache/rocketmq/dashboard/model/request/UserCreateRequest.java
index b0888d3..89ed851 100644
--- 
a/src/main/java/org/apache/rocketmq/dashboard/model/request/UserCreateRequest.java
+++ 
b/src/main/java/org/apache/rocketmq/dashboard/model/request/UserCreateRequest.java
@@ -23,6 +23,7 @@ import lombok.Setter;
 @Setter
 @Getter
 public class UserCreateRequest {
-    private String brokerAddress;
+    private String clusterName;
+    private String brokerName;
     private UserInfoParam userInfo;
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/model/request/UserUpdateRequest.java
 
b/src/main/java/org/apache/rocketmq/dashboard/model/request/UserUpdateRequest.java
index 9ef63f5..6c5caf7 100644
--- 
a/src/main/java/org/apache/rocketmq/dashboard/model/request/UserUpdateRequest.java
+++ 
b/src/main/java/org/apache/rocketmq/dashboard/model/request/UserUpdateRequest.java
@@ -23,6 +23,7 @@ import lombok.Setter;
 @Getter
 @Setter
 public class UserUpdateRequest {
-    private String brokerAddress;
+    private String clusterName;
+    private String brokerName;
     private UserInfoParam userInfo;
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/service/AclService.java 
b/src/main/java/org/apache/rocketmq/dashboard/service/AclService.java
index fcc340c..c9089ae 100644
--- a/src/main/java/org/apache/rocketmq/dashboard/service/AclService.java
+++ b/src/main/java/org/apache/rocketmq/dashboard/service/AclService.java
@@ -20,24 +20,21 @@ package org.apache.rocketmq.dashboard.service;
 
 import org.apache.rocketmq.dashboard.model.PolicyRequest;
 import org.apache.rocketmq.dashboard.model.request.UserInfoParam;
-import org.apache.rocketmq.remoting.protocol.body.UserInfo;
-
-import java.util.List;
 
 public interface AclService {
-    List<UserInfo> listUsers(String brokerAddress);
+    Object listUsers(String clusterName, String brokerAddress);
 
-    Object listAcls(String brokerAddress, String searchParam);
+    Object listAcls(String clusterName,String brokerAddress, String 
searchParam);
 
-    List<String> createAcl(PolicyRequest policyRequest);
+    Object createAcl(PolicyRequest policyRequest);
 
-    void deleteUser(String brokerAddress, String username);
+    void deleteUser(String clusterName,String brokerAddress, String username);
 
-    void updateUser(String brokerAddress, UserInfoParam userParam);
+    void updateUser(String clusterName,String brokerAddress, UserInfoParam 
userParam);
 
-    void createUser(String brokerAddress, UserInfoParam userParam);
+    void createUser(String clusterName,String brokerAddress, UserInfoParam 
userParam);
 
-    void deleteAcl(String brokerAddress, String subject, String resource);
+    void deleteAcl(String clusterName,String brokerAddress, String subject, 
String resource);
 
     void updateAcl(PolicyRequest policyRequest);
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/service/impl/AclServiceImpl.java 
b/src/main/java/org/apache/rocketmq/dashboard/service/impl/AclServiceImpl.java
index 52b9c94..cefcc44 100644
--- 
a/src/main/java/org/apache/rocketmq/dashboard/service/impl/AclServiceImpl.java
+++ 
b/src/main/java/org/apache/rocketmq/dashboard/service/impl/AclServiceImpl.java
@@ -21,9 +21,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.rocketmq.dashboard.model.Entry;
 import org.apache.rocketmq.dashboard.model.Policy;
 import org.apache.rocketmq.dashboard.model.PolicyRequest;
+import org.apache.rocketmq.dashboard.model.UserInfoDto;
 import org.apache.rocketmq.dashboard.model.request.UserInfoParam;
+import org.apache.rocketmq.dashboard.service.AbstractCommonService;
 import org.apache.rocketmq.dashboard.service.AclService;
+import org.apache.rocketmq.dashboard.service.ClusterInfoService;
 import org.apache.rocketmq.remoting.protocol.body.AclInfo;
+import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
 import org.apache.rocketmq.remoting.protocol.body.UserInfo;
 import org.apache.rocketmq.tools.admin.MQAdminExt;
 import org.slf4j.Logger;
@@ -37,7 +41,7 @@ import java.util.List;
 import java.util.Set;
 
 @Service
-public class AclServiceImpl implements AclService {
+public class AclServiceImpl extends AbstractCommonService implements 
AclService {
 
     private Logger logger = LoggerFactory.getLogger(AclServiceImpl.class);
 
@@ -45,60 +49,117 @@ public class AclServiceImpl implements AclService {
     @Autowired
     private MQAdminExt mqAdminExt;
 
+    @Autowired
+    private ClusterInfoService clusterInfoService;
 
-    @Override
-    public List<UserInfo> listUsers(String brokerAddress) {
-        List<UserInfo> userList;
-        try {
-            userList = mqAdminExt.listUser(brokerAddress, "");
-        } catch (Exception ex) {
-            logger.error("Failed to list users from broker: {}", 
brokerAddress, ex);
-            throw new RuntimeException("Failed to list users", ex);
-        }
-        if (userList == null || userList.isEmpty()) {
-            logger.warn("No users found for broker: {}", brokerAddress);
-            return new ArrayList<>();
-        }
-        return userList;
-    }
 
     @Override
-    public Object listAcls(String brokerAddress, String searchParam) {
-        List<AclInfo> aclList;
-        try {
-            String user = searchParam != null ? searchParam : "";
-            String res = searchParam != null ? searchParam : "";
-            aclList = mqAdminExt.listAcl(brokerAddress, user, "");
-            if (aclList == null) {
-                aclList = new ArrayList<>();
+    public List<UserInfoDto> listUsers(String clusterName, String brokerName) {
+
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+        Set<UserInfoDto> commonUsers = new HashSet<>();
+        final boolean[] firstIteration = {true};
+        brokerAddrList.forEach(address -> {
+            List<UserInfo> userList;
+            try {
+                userList = mqAdminExt.listUser(address, "");
+            } catch (Exception ex) {
+                logger.error("Failed to list users from broker: {}", address, 
ex);
+                throw new RuntimeException("Failed to list users", ex);
             }
-            List<AclInfo> resAclList = mqAdminExt.listAcl(brokerAddress, "", 
res);
-            if (resAclList != null) {
-                aclList.addAll(resAclList);
+
+            List<UserInfoDto> userListDtos = new ArrayList<>();
+            userList.forEach(user -> {
+                UserInfoDto userInfoDto = new UserInfoDto();
+                userListDtos.add(userInfoDto.setUserInfo(user));
+            });
+            if (!userList.isEmpty()) {
+                Set<UserInfoDto> currentUsers = new HashSet<>(userListDtos);
+                if (firstIteration[0]) {
+                    commonUsers.addAll(userListDtos);
+                    firstIteration[0] = false;
+                } else {
+                    commonUsers.retainAll(currentUsers);
+                }
+            } else {
+                logger.warn("No users found for broker: {}", address);
             }
-        } catch (Exception ex) {
-            logger.error("Failed to list ACLs from broker: {}", brokerAddress, 
ex);
-            throw new RuntimeException("Failed to list ACLs", ex);
-        }
-        ObjectMapper mapper = new ObjectMapper();
-        Set<String> uniqueAclStrings = new HashSet<>();
-        List<AclInfo> resultAclList = new ArrayList<>();
+        });
+
+        return new ArrayList<>(commonUsers);
+    }
 
-        for (AclInfo acl : aclList) {
+    @Override
+    public Object listAcls(String clusterName, String brokerName, String 
searchParam) {
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+        Set<org.apache.rocketmq.dashboard.model.AclInfo> commonAcls = new 
HashSet<>();
+        final boolean[] firstIteration = {true};
+        ObjectMapper mapper = new ObjectMapper(); // Initialize ObjectMapper 
once
+
+        brokerAddrList.forEach(address -> {
+            List<AclInfo> aclListForBroker;
             try {
-                String aclString = mapper.writeValueAsString(acl);
-                if (uniqueAclStrings.add(aclString)) {
-                    resultAclList.add(acl);
+                String user = searchParam != null ? searchParam : "";
+                String res = searchParam != null ? searchParam : "";
+                // Combine results from both listAcl calls for a single broker
+                List<AclInfo> byUser = mqAdminExt.listAcl(address, user, "");
+                List<AclInfo> byRes = mqAdminExt.listAcl(address, "", res);
+
+                aclListForBroker = new ArrayList<>();
+                if (byUser != null) {
+                    aclListForBroker.addAll(byUser);
+                }
+                if (byRes != null) {
+                    aclListForBroker.addAll(byRes);
+                }
+
+                // Deduplicate ACLs for the current broker to ensure accurate 
intersection
+                Set<AclInfo> uniqueAclsForBroker = new HashSet<>();
+                Set<String> uniqueAclStringsForBroker = new HashSet<>();
+                for (AclInfo acl : aclListForBroker) {
+                    try {
+                        String aclString = mapper.writeValueAsString(acl);
+                        if (uniqueAclStringsForBroker.add(aclString)) {
+                            uniqueAclsForBroker.add(acl);
+                        }
+                    } catch (Exception e) {
+                        logger.error("Error serializing AclInfo for broker {}: 
{}", address, e.getMessage());
+                    }
                 }
-            } catch (Exception e) {
-                logger.error("Error serializing AclInfo", e);
+                aclListForBroker = new ArrayList<>(uniqueAclsForBroker);
+
+            } catch (Exception ex) {
+                logger.error("Failed to list ACLs from broker: {}", address, 
ex);
+                throw new RuntimeException("Failed to list ACLs", ex);
             }
-        }
-        return resultAclList;
+            List<org.apache.rocketmq.dashboard.model.AclInfo> aclInfoList = 
new ArrayList<>();
+            aclListForBroker.forEach(acl -> {
+                org.apache.rocketmq.dashboard.model.AclInfo aclInfo = new 
org.apache.rocketmq.dashboard.model.AclInfo();
+                aclInfo.copyFrom(acl);
+                aclInfoList.add(aclInfo);
+            });
+            if (!aclListForBroker.isEmpty()) {
+                Set<org.apache.rocketmq.dashboard.model.AclInfo> currentAcls = 
new HashSet<>(aclInfoList);
+                if (firstIteration[0]) {
+                    commonAcls.addAll(currentAcls);
+                    firstIteration[0] = false;
+                } else {
+                    commonAcls.retainAll(currentAcls);
+                }
+            } else {
+                logger.warn("No ACLs found for broker: {}", address);
+                if (firstIteration[0]) {
+                    firstIteration[0] = false;
+                } else {
+                    commonAcls.clear(); // If any broker has no ACLs, the 
common set will be empty
+                }
+            }
+        });
+        return new ArrayList<>(commonAcls);
     }
 
     @Override
-    public List<String> createAcl(PolicyRequest policyRequest) {
+    public Object createAcl(PolicyRequest policyRequest) {
         List<String> successfulResources = new ArrayList<>();
 
         if (policyRequest == null || policyRequest.getPolicies() == null || 
policyRequest.getPolicies().isEmpty()) {
@@ -107,11 +168,13 @@ public class AclServiceImpl implements AclService {
         }
 
         String subject = policyRequest.getSubject();
-
         if (subject == null || subject.isEmpty()) {
             throw new IllegalArgumentException("Subject cannot be null or 
empty.");
         }
 
+        // Get the broker address list for creating ACLs on all relevant 
brokers
+        List<String> brokerAddrList = 
getBrokerAddressList(policyRequest.getClusterName(), 
policyRequest.getBrokerName());
+
         for (Policy policy : policyRequest.getPolicies()) {
             if (policy.getEntries() != null && !policy.getEntries().isEmpty()) 
{
                 for (Entry entry : policy.getEntries()) {
@@ -136,90 +199,110 @@ public class AclServiceImpl implements AclService {
                             aclInfo.setPolicies(aclPolicies);
                             aclInfo.setSubject(subject);
 
-                            try {
-                                logger.info("Attempting to create ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, 
policyRequest.getBrokerAddress());
-                                
mqAdminExt.createAcl(policyRequest.getBrokerAddress(), aclInfo);
-                                successfulResources.add(resource);
-                                logger.info("Successfully created ACL for 
subject: {}, resource: {}", subject, resource);
-                            } catch (Exception ex) {
-                                logger.error("Failed to create ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, 
policyRequest.getBrokerAddress(), ex);
-                                throw new RuntimeException("Failed to create 
ACL", ex);
+                            for (String brokerAddress : brokerAddrList) {
+                                try {
+                                    logger.info("Attempting to create ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, brokerAddress);
+                                    mqAdminExt.createAcl(brokerAddress, 
aclInfo);
+                                    logger.info("Successfully created ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, brokerAddress);
+                                } catch (Exception ex) {
+                                    throw new RuntimeException("Failed to 
create ACL on broker " + brokerAddress + ex.getMessage());
+                                }
                             }
                         }
                     }
                 }
             }
         }
-        return successfulResources;
+        return true;
     }
 
     @Override
-    public void deleteUser(String brokerAddress, String username) {
-        try {
-            mqAdminExt.deleteUser(brokerAddress, username);
-        } catch (Exception ex) {
-            logger.error("Failed to delete user: {} from broker: {}", 
username, brokerAddress, ex);
-            throw new RuntimeException("Failed to delete user", ex);
+    public void deleteUser(String clusterName, String brokerName, String 
username) {
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+
+        for (String address : brokerAddrList) {
+            try {
+                mqAdminExt.deleteUser(address, username);
+                logger.info("Successfully deleted user: {} from broker: {}", 
username, address);
+            } catch (Exception ex) {
+                logger.error("Failed to delete user: {} from broker: {}", 
username, address, ex);
+                throw new RuntimeException("Failed to delete user on broker " 
+ address + ex.getMessage());
+            }
         }
     }
 
     @Override
-    public void updateUser(String brokerAddress, UserInfoParam userParam) {
+    public void updateUser(String clusterName, String brokerName, 
UserInfoParam userParam) {
         UserInfo user = new UserInfo();
         user.setUsername(userParam.getUsername());
         user.setPassword(userParam.getPassword());
         user.setUserStatus(userParam.getUserStatus());
         user.setUserType(userParam.getUserType());
 
-        try {
-            mqAdminExt.updateUser(brokerAddress, user);
-        } catch (Exception ex) {
-            logger.error("Failed to update user: {} on broker: {}", 
userParam.getUsername(), brokerAddress, ex);
-            throw new RuntimeException("Failed to update user", ex);
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+
+        for (String address : brokerAddrList) {
+            try {
+                mqAdminExt.updateUser(address, user);
+                logger.info("Successfully updated user: {} on broker: {}", 
userParam.getUsername(), address);
+            } catch (Exception ex) {
+                logger.error("Failed to update user: {} on broker: {}", 
userParam.getUsername(), address, ex);
+                throw new RuntimeException("Failed to update user on broker " 
+ address + ex.getMessage());
+            }
         }
     }
 
     @Override
-    public void createUser(String brokerAddress, UserInfoParam userParam) {
+    public void createUser(String clusterName, String brokerName, 
UserInfoParam userParam) {
         UserInfo user = new UserInfo();
         user.setUsername(userParam.getUsername());
         user.setPassword(userParam.getPassword());
         user.setUserStatus(userParam.getUserStatus());
         user.setUserType(userParam.getUserType());
-        try {
-            mqAdminExt.createUser(brokerAddress, user);
-        } catch (Exception ex) {
-            logger.error("Failed to create user: {} on broker: {}", 
userParam.getUsername(), brokerAddress, ex);
-            throw new RuntimeException("Failed to create user", ex);
+
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+
+        for (String address : brokerAddrList) {
+            try {
+                mqAdminExt.createUser(address, user);
+                logger.info("Successfully created user: {} on broker: {}", 
userParam.getUsername(), address);
+            } catch (Exception ex) {
+                logger.error("Failed to create user: {} on broker: {}", 
userParam.getUsername(), address, ex);
+                throw new RuntimeException("Failed to create user on broker " 
+ address + ex.getMessage());
+            }
         }
     }
 
     @Override
-    public void deleteAcl(String brokerAddress, String subject, String 
resource) {
-        try {
-            String res = resource != null ? resource : "";
-            mqAdminExt.deleteAcl(brokerAddress, subject, res);
-        } catch (Exception ex) {
-            logger.error("Failed to delete ACL for subject: {} and resource: 
{} on broker: {}", subject, resource, brokerAddress, ex);
-            throw new RuntimeException("Failed to delete ACL", ex);
+    public void deleteAcl(String clusterName, String brokerName, String 
subject, String resource) {
+        List<String> brokerAddrList = getBrokerAddressList(clusterName, 
brokerName);
+        String res = resource != null ? resource : "";
+
+        for (String address : brokerAddrList) {
+            try {
+                mqAdminExt.deleteAcl(address, subject, res);
+                logger.info("Successfully deleted ACL for subject: {} and 
resource: {} on broker: {}", subject, resource, address);
+            } catch (Exception ex) {
+                logger.error("Failed to delete ACL for subject: {} and 
resource: {} on broker: {}", subject, resource, address, ex);
+                throw new RuntimeException("Failed to delete ACL on broker " + 
address + ex.getMessage());
+            }
         }
     }
 
     @Override
     public void updateAcl(PolicyRequest policyRequest) {
-
         if (policyRequest == null || policyRequest.getPolicies() == null || 
policyRequest.getPolicies().isEmpty()) {
             logger.warn("Policy request is null or policies list is empty. No 
ACLs to update.");
+            return;
         }
 
-        assert policyRequest != null;
-        String brokerAddress = policyRequest.getBrokerAddress();
         String subject = policyRequest.getSubject();
-
         if (subject == null || subject.isEmpty()) {
             throw new IllegalArgumentException("Subject cannot be null or 
empty.");
         }
 
+        List<String> brokerAddrList = 
getBrokerAddressList(policyRequest.getClusterName(), 
policyRequest.getBrokerName());
+
         for (Policy policy : policyRequest.getPolicies()) {
             if (policy.getEntries() != null && !policy.getEntries().isEmpty()) 
{
                 for (Entry entry : policy.getEntries()) {
@@ -244,18 +327,52 @@ public class AclServiceImpl implements AclService {
                             aclInfo.setPolicies(aclPolicies);
                             aclInfo.setSubject(subject);
 
-                            try {
-                                mqAdminExt.updateAcl(brokerAddress, aclInfo);
-                            } catch (Exception ex) {
-                                logger.error("Failed to update ACL for 
subject: {} on broker: {}", subject, brokerAddress, ex);
-                                throw new RuntimeException("Failed to update 
ACL", ex);
+                            for (String brokerAddress : brokerAddrList) {
+                                try {
+                                    mqAdminExt.updateAcl(brokerAddress, 
aclInfo);
+                                    logger.info("Successfully updated ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, brokerAddress);
+                                } catch (Exception ex) {
+                                    logger.error("Failed to update ACL for 
subject: {}, resource: {} on broker: {}", subject, resource, brokerAddress, ex);
+                                    throw new RuntimeException("Failed to 
update ACL on broker " + brokerAddress + ex.getMessage());
+                                }
                             }
                         }
                     }
                 }
             }
         }
+    }
 
+
+    public List<String> getBrokerAddressList(String clusterName, String 
brokerName) {
+        ClusterInfo clusterInfo = clusterInfoService.get();
+        List<String> brokerAddressList = new ArrayList<>();
+        if (brokerName != null) {
+            for (String brokerNameKey : 
changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
+                    new ArrayList<>(), List.of(brokerName))) {
+                clusterInfo.getBrokerAddrTable()
+                        .get(brokerNameKey)
+                        .getBrokerAddrs()
+                        .forEach((Long key, String value) -> 
brokerAddressList.add(value));
+            }
+        } else {
+            if (clusterName == null || clusterName.isEmpty()) {
+                logger.warn("Cluster name is null or empty. Cannot retrieve 
broker addresses.");
+                throw new IllegalArgumentException("Cluster name cannot be 
null or empty.");
+            }
+            if (clusterInfo == null || clusterInfo.getBrokerAddrTable() == 
null || clusterInfo.getBrokerAddrTable().isEmpty()) {
+                logger.warn("Cluster information is not available or has no 
broker addresses.");
+                throw new RuntimeException("Cluster information is not 
available or has no broker addresses.");
+            }
+            for (String brokerNameKey : 
changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
+                    List.of(clusterName), new ArrayList<>())) {
+                clusterInfo.getBrokerAddrTable()
+                        .get(brokerNameKey)
+                        .getBrokerAddrs()
+                        .forEach((Long key, String value) -> 
brokerAddressList.add(value));
+            }
+        }
+        return brokerAddressList;
     }
 
 }
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
 
b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
index 93b39e7..e307966 100644
--- 
a/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
+++ 
b/src/main/java/org/apache/rocketmq/dashboard/service/impl/ConsumerServiceImpl.java
@@ -210,7 +210,15 @@ public class ConsumerServiceImpl extends 
AbstractCommonService implements Consum
                     if (SYSTEM_GROUP_SET.contains(consumerGroup)) {
                         consumeInfo.setSubGroupType("SYSTEM");
                     } else {
-                        
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly()
 ? "FIFO" : "NORMAL");
+                        try {
+                            
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly()
 ? "FIFO" : "NORMAL");
+                        } catch (NullPointerException e) {
+                            logger.warn("SubscriptionGroupConfig not found for 
consumer group: {}", consumerGroup);
+                            boolean isFifoType = 
examineSubscriptionGroupConfig(consumerGroup)
+                                    
.stream().map(ConsumerConfigInfo::getSubscriptionGroupConfig)
+                                    
.allMatch(SubscriptionGroupConfig::isConsumeMessageOrderly);
+                            consumeInfo.setSubGroupType(isFifoType ? "FIFO" : 
"NORMAL");
+                        }
                     }
                     consumeInfo.setUpdateTime(new Date());
                     groupConsumeInfoList.add(consumeInfo);
@@ -275,17 +283,24 @@ public class ConsumerServiceImpl extends 
AbstractCommonService implements Consum
 
     @Override
     public List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String 
groupName, String address) {
-        ConsumeStats consumeStats;
+        List<ConsumeStats> consumeStatses = new ArrayList<>();
         String topic = null;
         try {
             String[] addresses = address.split(",");
-            String addr = addresses[0];
-            consumeStats = mqAdminExt.examineConsumeStats(addr, groupName, 
null, 3000);
+            for (String addr : addresses) {
+                consumeStatses.add(mqAdminExt.examineConsumeStats(addr, 
groupName, null, 3000));
+            }
         } catch (Exception e) {
             Throwables.throwIfUnchecked(e);
             throw new RuntimeException(e);
         }
-        return toTopicConsumerInfoList(topic, consumeStats, groupName);
+        List<TopicConsumerInfo> res = new ArrayList<>();
+        consumeStatses.forEach(consumeStats -> {
+            if (consumeStats != null && consumeStats.getOffsetTable() != null 
&& !consumeStats.getOffsetTable().isEmpty()) {
+                res.addAll(toTopicConsumerInfoList(topic, consumeStats, 
groupName));
+            }
+        });
+        return res;
     }
 
     @Override
diff --git 
a/src/main/java/org/apache/rocketmq/dashboard/service/provider/UserInfoProviderImpl.java
 
b/src/main/java/org/apache/rocketmq/dashboard/service/provider/UserInfoProviderImpl.java
index a5aaf6b..fd6a3f5 100644
--- 
a/src/main/java/org/apache/rocketmq/dashboard/service/provider/UserInfoProviderImpl.java
+++ 
b/src/main/java/org/apache/rocketmq/dashboard/service/provider/UserInfoProviderImpl.java
@@ -16,15 +16,16 @@
  */
 
 package org.apache.rocketmq.dashboard.service.provider;
+
 import org.apache.rocketmq.dashboard.service.ClusterInfoService;
 import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
 import org.apache.rocketmq.remoting.protocol.body.UserInfo;
 import org.apache.rocketmq.remoting.protocol.route.BrokerData;
 import org.apache.rocketmq.tools.admin.MQAdminExt;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 @Service
 public class UserInfoProviderImpl implements UserInfoProvider {
diff --git 
a/src/test/java/org/apache/rocketmq/dashboard/controller/AclControllerTest.java 
b/src/test/java/org/apache/rocketmq/dashboard/controller/AclControllerTest.java
index b8afa10..cf368d7 100644
--- 
a/src/test/java/org/apache/rocketmq/dashboard/controller/AclControllerTest.java
+++ 
b/src/test/java/org/apache/rocketmq/dashboard/controller/AclControllerTest.java
@@ -17,32 +17,33 @@
 package org.apache.rocketmq.dashboard.controller;
 
 
+import com.google.gson.Gson;
 import org.apache.rocketmq.auth.authentication.enums.UserStatus;
 import org.apache.rocketmq.auth.authentication.enums.UserType;
-import org.apache.rocketmq.auth.authorization.enums.Decision;
-import org.apache.rocketmq.dashboard.model.Policy;
-import org.apache.rocketmq.dashboard.model.PolicyRequest;
+import org.apache.rocketmq.dashboard.model.UserInfoDto;
 import org.apache.rocketmq.dashboard.model.request.UserCreateRequest;
 import org.apache.rocketmq.dashboard.model.request.UserInfoParam;
 import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
 import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
 import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
-import org.apache.rocketmq.remoting.protocol.body.AclInfo;
-import org.apache.rocketmq.remoting.protocol.body.UserInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-
 import java.util.Arrays;
 import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 public class AclControllerTest extends BaseControllerTest {
 
@@ -52,189 +53,193 @@ public class AclControllerTest extends BaseControllerTest 
{
     @InjectMocks
     private AclController aclController;
 
+    private final Gson gson = new Gson();
+
     @Before
     public void init() {
         MockitoAnnotations.initMocks(this);
         mockMvc = 
MockMvcBuilders.standaloneSetup(aclController).setControllerAdvice(GlobalExceptionHandler.class).build();
     }
 
-
     @Test
-    public void testListUsers() {
+    public void testListUsers() throws Exception {
         // Prepare test data
-        String brokerAddress = "localhost:10911";
-        List<UserInfo> expectedUsers = Arrays.asList(
-                UserInfo.of("user1", "password1", "super"),
-                UserInfo.of("user2", "password2", "super")
+        String clusterName = "test-cluster";
+        String brokerName = "localhost:10911";
+        List<UserInfoDto> expectedUsers = Arrays.asList(
+                new UserInfoDto("user1", "password1", "super","enable"),
+                new UserInfoDto("user2", "password2", "super","enable")
         );
 
         // Mock service behavior
-        when(aclService.listUsers(brokerAddress)).thenReturn(expectedUsers);
-
-        // Call controller method
-        List<UserInfo> result = aclController.listUsers(brokerAddress);
-
-        // Verify
-        assertEquals(expectedUsers, result);
-        verify(aclService, times(1)).listUsers(brokerAddress);
-    }
+        when(aclService.listUsers(clusterName, 
brokerName)).thenReturn(expectedUsers);
+
+        // Call controller method via MockMVC
+        mockMvc.perform(MockMvcRequestBuilders.get("/acl/users.query")
+                        .param("clusterName", clusterName)
+                        .param("brokerName", brokerName))
+                .andExpect(status().isOk())
+                .andExpect(result -> {
+                    List<UserInfoDto> actualUsers = 
gson.fromJson(result.getResponse().getContentAsString(), List.class);
+                    // Due to Gson's deserialization of List to LinkedTreeMap, 
direct assertEquals on List<UserInfo> won't work easily.
+                    // A more robust comparison would involve iterating or 
using a custom matcher if UserInfoDto doesn't override equals/hashCode.
+                    // For simplicity, let's assume UserInfoDto has proper 
equals/hashCode for now or convert to JSON string for comparison.
+                    assertEquals(gson.toJson(expectedUsers), 
result.getResponse().getContentAsString());
+                });
 
-    @Test
-    public void testListUsersWithoutBrokerAddress() {
-        // Prepare test data
-        List<UserInfo> expectedUsers = Arrays.asList(
-                UserInfo.of("user1", "password1", "super")
-        );
-
-        // Mock service behavior
-        when(aclService.listUsers(null)).thenReturn(expectedUsers);
-        // Call controller method
-        List<UserInfo> result = aclController.listUsers(null);
         // Verify
-        assertEquals(expectedUsers, result);
-        verify(aclService, times(1)).listUsers(null);
+        verify(aclService, times(1)).listUsers(clusterName, brokerName);
     }
 
     @Test
-    public void testListAcls() {
+    public void testListUsersWithoutBrokerAddressAndClusterName() throws 
Exception {
         // Prepare test data
-        String brokerAddress = "localhost:9092";
-        String searchParam = "user1";
-        Object expectedAcls = Arrays.asList(
-                AclInfo.of("user1", List.of("READ", "test"), 
List.of("TOPIC:test"), List.of("localhost:10911"), Decision.ALLOW.getName())
+        List<UserInfoDto> expectedUsers = Arrays.asList(
+                new UserInfoDto("user2", "password2", "super","enable")
         );
 
         // Mock service behavior
-        when(aclService.listAcls(brokerAddress, 
searchParam)).thenReturn(expectedAcls);
+        when(aclService.listUsers(null, null)).thenReturn(expectedUsers);
 
-        // Call controller method
-        Object result = aclController.listAcls(brokerAddress, searchParam);
+        // Call controller method via MockMVC
+        mockMvc.perform(MockMvcRequestBuilders.get("/acl/users.query"))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals(gson.toJson(expectedUsers), 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(expectedAcls, result);
-        verify(aclService, times(1)).listAcls(brokerAddress, searchParam);
+        verify(aclService, times(1)).listUsers(null, null);
     }
 
-    @Test
-    public void testCreateAcl() {
-        // Prepare test data
-        PolicyRequest request = new PolicyRequest();
-        request.setBrokerAddress("localhost:9092");
-        request.setSubject("user1");
-        request.setPolicies(List.of(
-                new Policy()
-        ));
 
-        // Call controller method
-        Object result = aclController.createAcl(request);
 
-        // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).createAcl(request);
-    }
 
     @Test
-    public void testDeleteUser() {
+    public void testDeleteUser() throws Exception {
         // Prepare test data
-        String brokerAddress = "localhost:9092";
+        String clusterName = "test-cluster";
+        String brokerName = "localhost:9092";
         String username = "user1";
 
-        // Call controller method
-        Object result = aclController.deleteUser(brokerAddress, username);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).deleteUser(clusterName, brokerName, 
username);
+
+        // Call controller method via MockMVC
+        mockMvc.perform(delete("/acl/deleteUser.do")
+                        .param("clusterName", clusterName)
+                        .param("brokerName", brokerName)
+                        .param("username", username))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).deleteUser(brokerAddress, username);
+        verify(aclService, times(1)).deleteUser(clusterName, brokerName, 
username);
     }
 
     @Test
-    public void testDeleteUserWithoutBrokerAddress() {
+    public void testDeleteUserWithoutBrokerAddressAndClusterName() throws 
Exception {
         // Prepare test data
         String username = "user1";
 
-        // Call controller method
-        Object result = aclController.deleteUser(null, username);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).deleteUser(null, null, username);
+
+        // Call controller method via MockMVC
+        mockMvc.perform(delete("/acl/deleteUser.do")
+                        .param("username", username))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).deleteUser(null, username);
+        verify(aclService, times(1)).deleteUser(null, null, username);
     }
 
     @Test
-    public void testUpdateUser() {
+    public void testUpdateUser() throws Exception {
         // Prepare test data
         UserUpdateRequest request = new UserUpdateRequest();
-        request.setBrokerAddress("localhost:9092");
+        request.setClusterName("test-cluster");
+        request.setBrokerName("localhost:9092");
         request.setUserInfo(new UserInfoParam("user1", "newPassword", 
UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
 
-        // Call controller method
-        Object result = aclController.updateUser(request);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).updateUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
+
+        // Call controller method via MockMVC
+        mockMvc.perform(MockMvcRequestBuilders.post("/acl/updateUser.do")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(gson.toJson(request)))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).updateUser(request.getBrokerAddress(), 
request.getUserInfo());
+        verify(aclService, times(1)).updateUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
     }
 
     @Test
-    public void testCreateUser() {
+    public void testCreateUser() throws Exception {
         // Prepare test data
         UserCreateRequest request = new UserCreateRequest();
-        request.setBrokerAddress("localhost:9092");
+        request.setClusterName("test-cluster");
+        request.setBrokerName("localhost:9092");
         request.setUserInfo(new UserInfoParam("user1", "newPassword", 
UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
 
-        // Call controller method
-        Object result = aclController.createUser(request);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).createUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
+
+        // Call controller method via MockMVC
+        mockMvc.perform(MockMvcRequestBuilders.post("/acl/createUser.do")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(gson.toJson(request)))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).createUser(request.getBrokerAddress(), 
request.getUserInfo());
+        verify(aclService, times(1)).createUser(request.getClusterName(), 
request.getBrokerName(), request.getUserInfo());
     }
 
     @Test
-    public void testDeleteAcl() {
+    public void testDeleteAcl() throws Exception {
         // Prepare test data
-        String brokerAddress = "localhost:9092";
+        String clusterName = "test-cluster";
+        String brokerName = "localhost:9092";
         String subject = "user1";
         String resource = "TOPIC:test";
 
-        // Call controller method
-        Object result = aclController.deleteAcl(brokerAddress, subject, 
resource);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).deleteAcl(clusterName, brokerName, 
subject, resource);
+
+        // Call controller method via MockMVC
+        mockMvc.perform(delete("/acl/deleteAcl.do")
+                        .param("clusterName", clusterName)
+                        .param("brokerName", brokerName)
+                        .param("subject", subject)
+                        .param("resource", resource))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).deleteAcl(brokerAddress, subject, 
resource);
+        verify(aclService, times(1)).deleteAcl(clusterName, brokerName, 
subject, resource);
     }
 
     @Test
-    public void testDeleteAclWithoutBrokerAddressAndResource() {
+    public void testDeleteAclWithoutBrokerAddressAndResourceAndClusterName() 
throws Exception {
         // Prepare test data
         String subject = "user1";
 
-        // Call controller method
-        Object result = aclController.deleteAcl(null, subject, null);
+        // Mock service behavior (void method)
+        doNothing().when(aclService).deleteAcl(null, null, subject, null);
+
+        // Call controller method via MockMVC
+        mockMvc.perform(delete("/acl/deleteAcl.do")
+                        .param("subject", subject))
+                .andExpect(status().isOk())
+                .andExpect(result -> assertEquals("true", 
result.getResponse().getContentAsString()));
 
         // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).deleteAcl(null, subject, null);
+        verify(aclService, times(1)).deleteAcl(null, null, subject, null);
     }
 
-    @Test
-    public void testUpdateAcl() {
-        // Prepare test data
-        PolicyRequest request = new PolicyRequest();
-        request.setBrokerAddress("localhost:9092");
-        request.setSubject("user1");
-        request.setPolicies(List.of(
-                new Policy()
-        ));
-
-        // Call controller method
-        Object result = aclController.updateAcl(request);
 
-        // Verify
-        assertEquals(true, result);
-        verify(aclService, times(1)).updateAcl(request);
-    }
 
     @Override
     protected Object getTestController() {

Reply via email to