没有编辑权限,隐藏集群属性;打印全部操作日志.

This commit is contained in:
许晓东
2023-08-20 20:04:47 +08:00
parent b529dc313e
commit f5b27d9b40
18 changed files with 159 additions and 19 deletions

View File

@@ -92,7 +92,7 @@ auth:
# 登录用户token的过期时间单位小时 # 登录用户token的过期时间单位小时
expire-hours: 24 expire-hours: 24
``` ```
默认有两个登录用户super-admin/123465admin/123456登录成功后在个人设置修改密码。super-admin和admin的唯一区别是super-admin可以增加删除用户admin不能。如果觉得不合适请在用户菜单下删除相关用户或角色自行创建合适的角色或用户。 默认有两个登录用户super-admin/123456admin/123456登录成功后在个人设置修改密码。super-admin和admin的唯一区别是super-admin可以增加删除用户admin不能。如果觉得不合适请在用户菜单下删除相关用户或角色自行创建合适的角色或用户。
注意:不开启登录认证,页面不显示用户菜单,正常现象。 注意:不开启登录认证,页面不显示用户菜单,正常现象。

View File

@@ -1,6 +1,9 @@
package com.xuxd.kafka.console.aspect; package com.xuxd.kafka.console.aspect;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog; import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.beans.Credentials;
import com.xuxd.kafka.console.config.LogConfig;
import com.xuxd.kafka.console.filter.CredentialsContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
@@ -12,6 +15,7 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@@ -28,6 +32,12 @@ public class ControllerLogAspect {
private ReentrantLock lock = new ReentrantLock(); private ReentrantLock lock = new ReentrantLock();
private final LogConfig logConfig;
public ControllerLogAspect(LogConfig logConfig) {
this.logConfig = logConfig;
}
@Pointcut("@annotation(com.xuxd.kafka.console.aspect.annotation.ControllerLog)") @Pointcut("@annotation(com.xuxd.kafka.console.aspect.annotation.ControllerLog)")
private void pointcut() { private void pointcut() {
@@ -35,6 +45,9 @@ public class ControllerLogAspect {
@Around("pointcut()") @Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable { public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
if (!logConfig.isPrintControllerLog()) {
return joinPoint.proceed();
}
StringBuilder params = new StringBuilder("["); StringBuilder params = new StringBuilder("[");
try { try {
String methodName = getMethodFullName(joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName()); String methodName = getMethodFullName(joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName());
@@ -56,6 +69,10 @@ public class ControllerLogAspect {
String resStr = "[" + (res != null ? res.toString() : "") + "]"; String resStr = "[" + (res != null ? res.toString() : "") + "]";
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Credentials credentials = CredentialsContext.get();
if (credentials != null) {
sb.append("[").append(credentials.getUsername()).append("] ");
}
String shortMethodName = descMap.getOrDefault(methodName, ".-"); String shortMethodName = descMap.getOrDefault(methodName, ".-");
shortMethodName = shortMethodName.substring(shortMethodName.lastIndexOf(".") + 1); shortMethodName = shortMethodName.substring(shortMethodName.lastIndexOf(".") + 1);
sb.append("[").append(shortMethodName) sb.append("[").append(shortMethodName)
@@ -85,6 +102,9 @@ public class ControllerLogAspect {
Class<?>[] clzArr = new Class[args.length]; Class<?>[] clzArr = new Class[args.length];
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
clzArr[i] = args[i].getClass(); clzArr[i] = args[i].getClass();
if (List.class.isAssignableFrom(clzArr[i])) {
clzArr[i] = List.class;
}
} }
method = aClass.getDeclaredMethod(methodName, clzArr); method = aClass.getDeclaredMethod(methodName, clzArr);

View File

@@ -101,18 +101,28 @@ public class PermissionAspect {
throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet); throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet);
} }
boolean unauthorized = true;
boolean notFoundHideProperty = true;
String roleIds = userDO.getRoleIds(); String roleIds = userDO.getRoleIds();
List<Long> roleIdList = Arrays.stream(roleIds.split(",")).map(String::trim).filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList()); List<Long> roleIdList = Arrays.stream(roleIds.split(",")).map(String::trim).filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList());
for (Long roleId : roleIdList) { for (Long roleId : roleIdList) {
Set<String> permSet = rolePermCache.getRolePermCache().getOrDefault(roleId, Collections.emptySet()); Set<String> permSet = rolePermCache.getRolePermCache().getOrDefault(roleId, Collections.emptySet());
for (String p : allowPermSet) { for (String p : allowPermSet) {
if (permSet.contains(p)) { if (permSet.contains(p)) {
return; unauthorized = false;
} }
} }
if (permSet.contains(authConfig.getHideClusterPropertyPerm())) {
notFoundHideProperty = false;
} }
}
if (unauthorized) {
throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet); throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet);
} }
if (authConfig.isHideClusterProperty() && notFoundHideProperty) {
credentials.setHideClusterProperty(true);
}
}
private Map<String, Set<String>> checkPermMap(String methodName, String[] value) { private Map<String, Set<String>> checkPermMap(String methodName, String[] value) {
if (!permMap.containsKey(methodName)) { if (!permMap.containsKey(methodName)) {

View File

@@ -15,6 +15,11 @@ public class Credentials {
private long expiration; private long expiration;
/**
* 是否隐藏集群属性
*/
private boolean hideClusterProperty;
public boolean isInvalid() { public boolean isInvalid() {
return this == INVALID; return this == INVALID;
} }

View File

@@ -18,4 +18,8 @@ public class AuthConfig {
private String secret = "kafka-console-ui-default-secret"; private String secret = "kafka-console-ui-default-secret";
private long expireHours; private long expireHours;
private boolean hideClusterProperty;
private String hideClusterPropertyPerm = "op:cluster-switch:edit";
} }

View File

@@ -0,0 +1,23 @@
package com.xuxd.kafka.console.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author: xuxd
* @since: 2023/8/20 20:00
**/
@Configuration
@ConfigurationProperties(prefix = "log")
public class LogConfig {
private boolean printControllerLog = true;
public boolean isPrintControllerLog() {
return printControllerLog;
}
public void setPrintControllerLog(boolean printControllerLog) {
this.printControllerLog = printControllerLog;
}
}

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.AclEntry; import com.xuxd.kafka.console.beans.AclEntry;
import com.xuxd.kafka.console.beans.dto.AddAuthDTO; import com.xuxd.kafka.console.beans.dto.AddAuthDTO;
@@ -46,6 +47,7 @@ public class AclAuthController {
return aclService.getAclList(param.toEntry()); return aclService.getAclList(param.toEntry());
} }
@ControllerLog("增加Acl")
@Permission({"acl:authority:add-principal", "acl:authority:add", "acl:sasl-scram:add-auth"}) @Permission({"acl:authority:add-principal", "acl:authority:add", "acl:sasl-scram:add-auth"})
@PostMapping @PostMapping
public Object addAcl(@RequestBody AddAuthDTO param) { public Object addAcl(@RequestBody AddAuthDTO param) {
@@ -58,6 +60,7 @@ public class AclAuthController {
* @param param entry.topic && entry.username must. * @param param entry.topic && entry.username must.
* @return * @return
*/ */
@ControllerLog("增加ProducerAcl")
@Permission({"acl:authority:producer", "acl:sasl-scram:producer"}) @Permission({"acl:authority:producer", "acl:sasl-scram:producer"})
@PostMapping("/producer") @PostMapping("/producer")
public Object addProducerAcl(@RequestBody ProducerAuthDTO param) { public Object addProducerAcl(@RequestBody ProducerAuthDTO param) {
@@ -71,6 +74,7 @@ public class AclAuthController {
* @param param entry.topic && entry.groupId entry.username must. * @param param entry.topic && entry.groupId entry.username must.
* @return * @return
*/ */
@ControllerLog("增加ConsumerAcl")
@Permission({"acl:authority:consumer", "acl:sasl-scram:consumer"}) @Permission({"acl:authority:consumer", "acl:sasl-scram:consumer"})
@PostMapping("/consumer") @PostMapping("/consumer")
public Object addConsumerAcl(@RequestBody ConsumerAuthDTO param) { public Object addConsumerAcl(@RequestBody ConsumerAuthDTO param) {
@@ -84,6 +88,7 @@ public class AclAuthController {
* @param entry entry * @param entry entry
* @return * @return
*/ */
@ControllerLog("删除Acl")
@Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @Permission({"acl:authority:clean", "acl:sasl-scram:pure"})
@DeleteMapping @DeleteMapping
public Object deleteAclByUser(@RequestBody AclEntry entry) { public Object deleteAclByUser(@RequestBody AclEntry entry) {
@@ -96,6 +101,7 @@ public class AclAuthController {
* @param param entry.username * @param param entry.username
* @return * @return
*/ */
@ControllerLog("删除Acl")
@Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @Permission({"acl:authority:clean", "acl:sasl-scram:pure"})
@DeleteMapping("/user") @DeleteMapping("/user")
public Object deleteAclByUser(@RequestBody DeleteAclDTO param) { public Object deleteAclByUser(@RequestBody DeleteAclDTO param) {
@@ -103,11 +109,12 @@ public class AclAuthController {
} }
/** /**
* add producer acl. * delete producer acl.
* *
* @param param entry.topic && entry.username must. * @param param entry.topic && entry.username must.
* @return * @return
*/ */
@ControllerLog("删除ProducerAcl")
@Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @Permission({"acl:authority:clean", "acl:sasl-scram:pure"})
@DeleteMapping("/producer") @DeleteMapping("/producer")
public Object deleteProducerAcl(@RequestBody ProducerAuthDTO param) { public Object deleteProducerAcl(@RequestBody ProducerAuthDTO param) {
@@ -116,11 +123,12 @@ public class AclAuthController {
} }
/** /**
* add consumer acl. * delete consumer acl.
* *
* @param param entry.topic && entry.groupId entry.username must. * @param param entry.topic && entry.groupId entry.username must.
* @return * @return
*/ */
@ControllerLog("删除ConsumerAcl")
@Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @Permission({"acl:authority:clean", "acl:sasl-scram:pure"})
@DeleteMapping("/consumer") @DeleteMapping("/consumer")
public Object deleteConsumerAcl(@RequestBody ConsumerAuthDTO param) { public Object deleteConsumerAcl(@RequestBody ConsumerAuthDTO param) {
@@ -134,6 +142,7 @@ public class AclAuthController {
* @param param acl principal. * @param param acl principal.
* @return true or false. * @return true or false.
*/ */
@ControllerLog("清除Acl")
@Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @Permission({"acl:authority:clean", "acl:sasl-scram:pure"})
@DeleteMapping("/clear") @DeleteMapping("/clear")
public Object clearAcl(@RequestBody DeleteAclDTO param) { public Object clearAcl(@RequestBody DeleteAclDTO param) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.AclEntry; import com.xuxd.kafka.console.beans.AclEntry;
import com.xuxd.kafka.console.beans.AclUser; import com.xuxd.kafka.console.beans.AclUser;
@@ -33,12 +34,14 @@ public class AclUserController {
return aclService.getUserList(); return aclService.getUserList();
} }
@ControllerLog("增加SaslUser")
@Permission({"acl:sasl-scram:add-update", "acl:sasl-scram:add-auth"}) @Permission({"acl:sasl-scram:add-update", "acl:sasl-scram:add-auth"})
@PostMapping @PostMapping
public Object addOrUpdateUser(@RequestBody AclUser user) { public Object addOrUpdateUser(@RequestBody AclUser user) {
return aclService.addOrUpdateUser(user.getUsername(), user.getPassword()); return aclService.addOrUpdateUser(user.getUsername(), user.getPassword());
} }
@ControllerLog("删除SaslUser")
@Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"}) @Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"})
@DeleteMapping @DeleteMapping
public Object deleteUser(@RequestBody AclUser user) { public Object deleteUser(@RequestBody AclUser user) {
@@ -46,6 +49,7 @@ public class AclUserController {
} }
@ControllerLog("删除SaslUser和Acl")
@Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"}) @Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"})
@DeleteMapping("/auth") @DeleteMapping("/auth")
public Object deleteUserAndAuth(@RequestBody AclUser user) { public Object deleteUserAndAuth(@RequestBody AclUser user) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.ResponseData;
import com.xuxd.kafka.console.beans.dto.AlterClientQuotaDTO; import com.xuxd.kafka.console.beans.dto.AlterClientQuotaDTO;
@@ -28,6 +29,7 @@ public class ClientQuotaController {
return clientQuotaService.getClientQuotaConfigs(request.getTypes(), request.getNames()); return clientQuotaService.getClientQuotaConfigs(request.getTypes(), request.getNames());
} }
@ControllerLog("增加限流配额")
@Permission({"quota:user:add", "quota:client:add", "quota:user-client:add", "quota:edit"}) @Permission({"quota:user:add", "quota:client:add", "quota:user-client:add", "quota:edit"})
@PostMapping @PostMapping
public Object alterClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) { public Object alterClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) {
@@ -41,6 +43,7 @@ public class ClientQuotaController {
return clientQuotaService.alterClientQuotaConfigs(request); return clientQuotaService.alterClientQuotaConfigs(request);
} }
@ControllerLog("删除限流配额")
@Permission("quota:del") @Permission("quota:del")
@DeleteMapping @DeleteMapping
public Object deleteClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) { public Object deleteClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.dto.ClusterInfoDTO; import com.xuxd.kafka.console.beans.dto.ClusterInfoDTO;
import com.xuxd.kafka.console.service.ClusterService; import com.xuxd.kafka.console.service.ClusterService;
@@ -30,23 +31,27 @@ public class ClusterController {
return clusterService.getClusterInfo(); return clusterService.getClusterInfo();
} }
@Permission("op:cluster-switch")
@GetMapping("/info") @GetMapping("/info")
public Object getClusterInfoList() { public Object getClusterInfoList() {
return clusterService.getClusterInfoList(); return clusterService.getClusterInfoList();
} }
@ControllerLog("增加集群信息")
@Permission("op:cluster-switch:add") @Permission("op:cluster-switch:add")
@PostMapping("/info") @PostMapping("/info")
public Object addClusterInfo(@RequestBody ClusterInfoDTO dto) { public Object addClusterInfo(@RequestBody ClusterInfoDTO dto) {
return clusterService.addClusterInfo(dto.to()); return clusterService.addClusterInfo(dto.to());
} }
@ControllerLog("删除集群信息")
@Permission("op:cluster-switch:del") @Permission("op:cluster-switch:del")
@DeleteMapping("/info") @DeleteMapping("/info")
public Object deleteClusterInfo(@RequestBody ClusterInfoDTO dto) { public Object deleteClusterInfo(@RequestBody ClusterInfoDTO dto) {
return clusterService.deleteClusterInfo(dto.getId()); return clusterService.deleteClusterInfo(dto.getId());
} }
@ControllerLog("编辑集群信息")
@Permission("op:cluster-switch:edit") @Permission("op:cluster-switch:edit")
@PutMapping("/info") @PutMapping("/info")
public Object updateClusterInfo(@RequestBody ClusterInfoDTO dto) { public Object updateClusterInfo(@RequestBody ClusterInfoDTO dto) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.ResponseData;
import com.xuxd.kafka.console.beans.dto.AlterConfigDTO; import com.xuxd.kafka.console.beans.dto.AlterConfigDTO;
@@ -48,12 +49,14 @@ public class ConfigController {
return configService.getTopicConfig(topic); return configService.getTopicConfig(topic);
} }
@ControllerLog("编辑topic配置")
@Permission("topic:property-config:edit") @Permission("topic:property-config:edit")
@PostMapping("/topic") @PostMapping("/topic")
public Object setTopicConfig(@RequestBody AlterConfigDTO dto) { public Object setTopicConfig(@RequestBody AlterConfigDTO dto) {
return configService.alterTopicConfig(dto.getEntity(), dto.to(), AlterType.SET); return configService.alterTopicConfig(dto.getEntity(), dto.to(), AlterType.SET);
} }
@ControllerLog("删除topic配置")
@Permission("topic:property-config:del") @Permission("topic:property-config:del")
@DeleteMapping("/topic") @DeleteMapping("/topic")
public Object deleteTopicConfig(@RequestBody AlterConfigDTO dto) { public Object deleteTopicConfig(@RequestBody AlterConfigDTO dto) {
@@ -66,12 +69,14 @@ public class ConfigController {
return configService.getBrokerConfig(brokerId); return configService.getBrokerConfig(brokerId);
} }
@ControllerLog("设置broker配置")
@Permission("cluster:edit") @Permission("cluster:edit")
@PostMapping("/broker") @PostMapping("/broker")
public Object setBrokerConfig(@RequestBody AlterConfigDTO dto) { public Object setBrokerConfig(@RequestBody AlterConfigDTO dto) {
return configService.alterBrokerConfig(dto.getEntity(), dto.to(), AlterType.SET); return configService.alterBrokerConfig(dto.getEntity(), dto.to(), AlterType.SET);
} }
@ControllerLog("编辑broker配置")
@Permission("cluster:edit") @Permission("cluster:edit")
@DeleteMapping("/broker") @DeleteMapping("/broker")
public Object deleteBrokerConfig(@RequestBody AlterConfigDTO dto) { public Object deleteBrokerConfig(@RequestBody AlterConfigDTO dto) {
@@ -84,12 +89,14 @@ public class ConfigController {
return configService.getBrokerLoggerConfig(brokerId); return configService.getBrokerLoggerConfig(brokerId);
} }
@ControllerLog("编辑broker日志配置")
@Permission("cluster:edit") @Permission("cluster:edit")
@PostMapping("/broker/logger") @PostMapping("/broker/logger")
public Object setBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) { public Object setBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) {
return configService.alterBrokerLoggerConfig(dto.getEntity(), dto.to(), AlterType.SET); return configService.alterBrokerLoggerConfig(dto.getEntity(), dto.to(), AlterType.SET);
} }
@ControllerLog("删除broker日志配置")
@Permission("cluster:edit") @Permission("cluster:edit")
@DeleteMapping("/broker/logger") @DeleteMapping("/broker/logger")
public Object deleteBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) { public Object deleteBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.ResponseData;
import com.xuxd.kafka.console.beans.dto.AddSubscriptionDTO; import com.xuxd.kafka.console.beans.dto.AddSubscriptionDTO;
@@ -43,6 +44,7 @@ public class ConsumerController {
return consumerService.getConsumerGroupList(groupIdList, stateSet); return consumerService.getConsumerGroupList(groupIdList, stateSet);
} }
@ControllerLog("删除消费组")
@Permission("group:del") @Permission("group:del")
@DeleteMapping("/group") @DeleteMapping("/group")
public Object deleteConsumerGroup(@RequestParam("groupId") String groupId) { public Object deleteConsumerGroup(@RequestParam("groupId") String groupId) {
@@ -61,12 +63,14 @@ public class ConsumerController {
return consumerService.getConsumerDetail(groupId); return consumerService.getConsumerDetail(groupId);
} }
@ControllerLog("新增消费组")
@Permission("group:add") @Permission("group:add")
@PostMapping("/subscription") @PostMapping("/subscription")
public Object addSubscription(@RequestBody AddSubscriptionDTO subscriptionDTO) { public Object addSubscription(@RequestBody AddSubscriptionDTO subscriptionDTO) {
return consumerService.addSubscription(subscriptionDTO.getGroupId(), subscriptionDTO.getTopic()); return consumerService.addSubscription(subscriptionDTO.getGroupId(), subscriptionDTO.getTopic());
} }
@ControllerLog("重置消费位点")
@Permission({"group:consumer-detail:min", @Permission({"group:consumer-detail:min",
"group:consumer-detail:last", "group:consumer-detail:last",
"group:consumer-detail:timestamp", "group:consumer-detail:timestamp",

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.QueryMessage; import com.xuxd.kafka.console.beans.QueryMessage;
import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.ResponseData;
@@ -48,18 +49,21 @@ public class MessageController {
return messageService.deserializerList(); return messageService.deserializerList();
} }
@ControllerLog("在线发送消息")
@Permission("message:send") @Permission("message:send")
@PostMapping("/send") @PostMapping("/send")
public Object send(@RequestBody SendMessage message) { public Object send(@RequestBody SendMessage message) {
return messageService.send(message); return messageService.send(message);
} }
@ControllerLog("重新发送消息")
@Permission("message:resend") @Permission("message:resend")
@PostMapping("/resend") @PostMapping("/resend")
public Object resend(@RequestBody SendMessage message) { public Object resend(@RequestBody SendMessage message) {
return messageService.resend(message); return messageService.resend(message);
} }
@ControllerLog("在线删除消息")
@Permission("message:del") @Permission("message:del")
@DeleteMapping @DeleteMapping
public Object delete(@RequestBody List<QueryMessage> messages) { public Object delete(@RequestBody List<QueryMessage> messages) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.TopicPartition; import com.xuxd.kafka.console.beans.TopicPartition;
import com.xuxd.kafka.console.beans.dto.BrokerThrottleDTO; import com.xuxd.kafka.console.beans.dto.BrokerThrottleDTO;
@@ -24,12 +25,14 @@ public class OperationController {
@Autowired @Autowired
private OperationService operationService; private OperationService operationService;
@ControllerLog("同步消费位点")
@PostMapping("/sync/consumer/offset") @PostMapping("/sync/consumer/offset")
public Object syncConsumerOffset(@RequestBody SyncDataDTO dto) { public Object syncConsumerOffset(@RequestBody SyncDataDTO dto) {
dto.getProperties().put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, dto.getAddress()); dto.getProperties().put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, dto.getAddress());
return operationService.syncConsumerOffset(dto.getGroupId(), dto.getTopic(), dto.getProperties()); return operationService.syncConsumerOffset(dto.getGroupId(), dto.getTopic(), dto.getProperties());
} }
@ControllerLog("重新位点对齐")
@PostMapping("/sync/min/offset/alignment") @PostMapping("/sync/min/offset/alignment")
public Object minOffsetAlignment(@RequestBody SyncDataDTO dto) { public Object minOffsetAlignment(@RequestBody SyncDataDTO dto) {
dto.getProperties().put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, dto.getAddress()); dto.getProperties().put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, dto.getAddress());
@@ -41,23 +44,27 @@ public class OperationController {
return operationService.getAlignmentList(); return operationService.getAlignmentList();
} }
@ControllerLog("deleteAlignment")
@DeleteMapping("/sync/alignment") @DeleteMapping("/sync/alignment")
public Object deleteAlignment(@RequestParam("id") Long id) { public Object deleteAlignment(@RequestParam("id") Long id) {
return operationService.deleteAlignmentById(id); return operationService.deleteAlignmentById(id);
} }
@ControllerLog("优先副本leader")
@Permission({"topic:partition-detail:preferred", "op:replication-preferred"}) @Permission({"topic:partition-detail:preferred", "op:replication-preferred"})
@PostMapping("/replication/preferred") @PostMapping("/replication/preferred")
public Object electPreferredLeader(@RequestBody ReplicationDTO dto) { public Object electPreferredLeader(@RequestBody ReplicationDTO dto) {
return operationService.electPreferredLeader(dto.getTopic(), dto.getPartition()); return operationService.electPreferredLeader(dto.getTopic(), dto.getPartition());
} }
@ControllerLog("配置同步限流")
@Permission("op:config-throttle") @Permission("op:config-throttle")
@PostMapping("/broker/throttle") @PostMapping("/broker/throttle")
public Object configThrottle(@RequestBody BrokerThrottleDTO dto) { public Object configThrottle(@RequestBody BrokerThrottleDTO dto) {
return operationService.configThrottle(dto.getBrokerList(), dto.getUnit().toKb(dto.getThrottle())); return operationService.configThrottle(dto.getBrokerList(), dto.getUnit().toKb(dto.getThrottle()));
} }
@ControllerLog("移除限流配置")
@Permission("op:remove-throttle") @Permission("op:remove-throttle")
@DeleteMapping("/broker/throttle") @DeleteMapping("/broker/throttle")
public Object removeThrottle(@RequestBody BrokerThrottleDTO dto) { public Object removeThrottle(@RequestBody BrokerThrottleDTO dto) {
@@ -70,6 +77,7 @@ public class OperationController {
return operationService.currentReassignments(); return operationService.currentReassignments();
} }
@ControllerLog("取消副本重分配")
@Permission("op:replication-update-detail:cancel") @Permission("op:replication-update-detail:cancel")
@DeleteMapping("/replication/reassignments") @DeleteMapping("/replication/reassignments")
public Object cancelReassignment(@RequestBody TopicPartition partition) { public Object cancelReassignment(@RequestBody TopicPartition partition) {

View File

@@ -1,5 +1,6 @@
package com.xuxd.kafka.console.controller; package com.xuxd.kafka.console.controller;
import com.xuxd.kafka.console.aspect.annotation.ControllerLog;
import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.aspect.annotation.Permission;
import com.xuxd.kafka.console.beans.ReplicaAssignment; import com.xuxd.kafka.console.beans.ReplicaAssignment;
import com.xuxd.kafka.console.beans.dto.AddPartitionDTO; import com.xuxd.kafka.console.beans.dto.AddPartitionDTO;
@@ -39,6 +40,7 @@ public class TopicController {
return topicService.getTopicList(topic, TopicType.valueOf(type.toUpperCase())); return topicService.getTopicList(topic, TopicType.valueOf(type.toUpperCase()));
} }
@ControllerLog("删除topic")
@Permission({"topic:batch-del", "topic:del"}) @Permission({"topic:batch-del", "topic:del"})
@DeleteMapping @DeleteMapping
public Object deleteTopic(@RequestBody List<String> topics) { public Object deleteTopic(@RequestBody List<String> topics) {
@@ -51,12 +53,14 @@ public class TopicController {
return topicService.getTopicPartitionInfo(topic.trim()); return topicService.getTopicPartitionInfo(topic.trim());
} }
@ControllerLog("创建topic")
@Permission("topic:add") @Permission("topic:add")
@PostMapping("/new") @PostMapping("/new")
public Object createNewTopic(@RequestBody NewTopicDTO topicDTO) { public Object createNewTopic(@RequestBody NewTopicDTO topicDTO) {
return topicService.createTopic(topicDTO.toNewTopic()); return topicService.createTopic(topicDTO.toNewTopic());
} }
@ControllerLog("增加topic分区")
@Permission("topic:partition-add") @Permission("topic:partition-add")
@PostMapping("/partition/new") @PostMapping("/partition/new")
public Object addPartition(@RequestBody AddPartitionDTO partitionDTO) { public Object addPartition(@RequestBody AddPartitionDTO partitionDTO) {
@@ -80,12 +84,14 @@ public class TopicController {
return topicService.getCurrentReplicaAssignment(topic); return topicService.getCurrentReplicaAssignment(topic);
} }
@ControllerLog("更新副本")
@Permission({"topic:replication-modify", "op:replication-reassign"}) @Permission({"topic:replication-modify", "op:replication-reassign"})
@PostMapping("/replica/assignment") @PostMapping("/replica/assignment")
public Object updateReplicaAssignment(@RequestBody ReplicaAssignment assignment) { public Object updateReplicaAssignment(@RequestBody ReplicaAssignment assignment) {
return topicService.updateReplicaAssignment(assignment); return topicService.updateReplicaAssignment(assignment);
} }
@ControllerLog("配置限流")
@Permission("topic:replication-sync-throttle") @Permission("topic:replication-sync-throttle")
@PostMapping("/replica/throttle") @PostMapping("/replica/throttle")
public Object configThrottle(@RequestBody TopicThrottleDTO dto) { public Object configThrottle(@RequestBody TopicThrottleDTO dto) {

View File

@@ -3,15 +3,14 @@ package com.xuxd.kafka.console.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xuxd.kafka.console.beans.BrokerNode; import com.xuxd.kafka.console.beans.BrokerNode;
import com.xuxd.kafka.console.beans.ClusterInfo; import com.xuxd.kafka.console.beans.ClusterInfo;
import com.xuxd.kafka.console.beans.Credentials;
import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.ResponseData;
import com.xuxd.kafka.console.beans.dos.ClusterInfoDO; import com.xuxd.kafka.console.beans.dos.ClusterInfoDO;
import com.xuxd.kafka.console.beans.vo.BrokerApiVersionVO; import com.xuxd.kafka.console.beans.vo.BrokerApiVersionVO;
import com.xuxd.kafka.console.beans.vo.ClusterInfoVO; import com.xuxd.kafka.console.beans.vo.ClusterInfoVO;
import com.xuxd.kafka.console.dao.ClusterInfoMapper; import com.xuxd.kafka.console.dao.ClusterInfoMapper;
import com.xuxd.kafka.console.filter.CredentialsContext;
import com.xuxd.kafka.console.service.ClusterService; import com.xuxd.kafka.console.service.ClusterService;
import java.util.*;
import java.util.stream.Collectors;
import kafka.console.ClusterConsole; import kafka.console.ClusterConsole;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@@ -21,6 +20,9 @@ import org.apache.kafka.common.Node;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* kafka-console-ui. * kafka-console-ui.
* *
@@ -41,7 +43,8 @@ public class ClusterServiceImpl implements ClusterService {
this.clusterInfoMapper = clusterInfoMapper.getIfAvailable(); this.clusterInfoMapper = clusterInfoMapper.getIfAvailable();
} }
@Override public ResponseData getClusterInfo() { @Override
public ResponseData getClusterInfo() {
ClusterInfo clusterInfo = clusterConsole.clusterInfo(); ClusterInfo clusterInfo = clusterConsole.clusterInfo();
Set<BrokerNode> nodes = clusterInfo.getNodes(); Set<BrokerNode> nodes = clusterInfo.getNodes();
if (nodes == null) { if (nodes == null) {
@@ -52,12 +55,22 @@ public class ClusterServiceImpl implements ClusterService {
return ResponseData.create().data(clusterInfo).success(); return ResponseData.create().data(clusterInfo).success();
} }
@Override public ResponseData getClusterInfoList() { @Override
public ResponseData getClusterInfoList() {
// 如果开启权限管理,当前用户没有集群切换->集群信息的编辑权限隐藏集群的属性信息避免ACL属性暴露出来
Credentials credentials = CredentialsContext.get();
return ResponseData.create().data(clusterInfoMapper.selectList(null) return ResponseData.create().data(clusterInfoMapper.selectList(null)
.stream().map(ClusterInfoVO::from).collect(Collectors.toList())).success(); .stream().map(e -> {
ClusterInfoVO vo = ClusterInfoVO.from(e);
if (credentials != null && credentials.isHideClusterProperty()) {
vo.setProperties(Collections.emptyList());
}
return vo;
}).collect(Collectors.toList())).success();
} }
@Override public ResponseData addClusterInfo(ClusterInfoDO infoDO) { @Override
public ResponseData addClusterInfo(ClusterInfoDO infoDO) {
QueryWrapper<ClusterInfoDO> queryWrapper = new QueryWrapper<>(); QueryWrapper<ClusterInfoDO> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cluster_name", infoDO.getClusterName()); queryWrapper.eq("cluster_name", infoDO.getClusterName());
if (clusterInfoMapper.selectCount(queryWrapper) > 0) { if (clusterInfoMapper.selectCount(queryWrapper) > 0) {
@@ -67,12 +80,14 @@ public class ClusterServiceImpl implements ClusterService {
return ResponseData.create().success(); return ResponseData.create().success();
} }
@Override public ResponseData deleteClusterInfo(Long id) { @Override
public ResponseData deleteClusterInfo(Long id) {
clusterInfoMapper.deleteById(id); clusterInfoMapper.deleteById(id);
return ResponseData.create().success(); return ResponseData.create().success();
} }
@Override public ResponseData updateClusterInfo(ClusterInfoDO infoDO) { @Override
public ResponseData updateClusterInfo(ClusterInfoDO infoDO) {
if (infoDO.getProperties() == null) { if (infoDO.getProperties() == null) {
// null 的话不更新这个是bug设置为空字符串解决 // null 的话不更新这个是bug设置为空字符串解决
infoDO.setProperties(""); infoDO.setProperties("");
@@ -81,7 +96,8 @@ public class ClusterServiceImpl implements ClusterService {
return ResponseData.create().success(); return ResponseData.create().success();
} }
@Override public ResponseData peekClusterInfo() { @Override
public ResponseData peekClusterInfo() {
List<ClusterInfoDO> dos = clusterInfoMapper.selectList(null); List<ClusterInfoDO> dos = clusterInfoMapper.selectList(null);
if (CollectionUtils.isEmpty(dos)) { if (CollectionUtils.isEmpty(dos)) {
return ResponseData.create().failed("No Cluster Info."); return ResponseData.create().failed("No Cluster Info.");
@@ -89,7 +105,8 @@ public class ClusterServiceImpl implements ClusterService {
return ResponseData.create().data(dos.stream().findFirst().map(ClusterInfoVO::from)).success(); return ResponseData.create().data(dos.stream().findFirst().map(ClusterInfoVO::from)).success();
} }
@Override public ResponseData getBrokerApiVersionInfo() { @Override
public ResponseData getBrokerApiVersionInfo() {
HashMap<Node, NodeApiVersions> map = clusterConsole.listBrokerVersionInfo(); HashMap<Node, NodeApiVersions> map = clusterConsole.listBrokerVersionInfo();
List<BrokerApiVersionVO> list = new ArrayList<>(map.size()); List<BrokerApiVersionVO> list = new ArrayList<>(map.size());
map.forEach(((node, versions) -> { map.forEach(((node, versions) -> {

View File

@@ -53,3 +53,9 @@ auth:
enable: false enable: false
# 登录用户token的过期时间单位小时 # 登录用户token的过期时间单位小时
expire-hours: 24 expire-hours: 24
# 隐藏集群的属性信息如果当前用户没有集群切换里的编辑权限就不能看集群的属性信息有开启ACL的集群需要开启这个
hide-cluster-property: true
log:
# 是否打印操作日志(增加、删除、编辑)
print-controller-log: true

View File

@@ -45,7 +45,12 @@
<appender-ref ref="AsyncFileAppender"/> <appender-ref ref="AsyncFileAppender"/>
</logger> </logger>
<logger name="org.apache.kafka.clients.consumer" level="warn" additivity="false"> <logger name="org.apache.kafka.common" level="warn" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="AsyncFileAppender"/>
</logger>
<logger name="org.apache.kafka.clients.Metadata" level="warn" additivity="false">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
<appender-ref ref="AsyncFileAppender"/> <appender-ref ref="AsyncFileAppender"/>
</logger> </logger>