From 571efe6ddc91a75e2a792eeb5f85ab61c68c72c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=99=93=E4=B8=9C?= <763795151@qq.com> Date: Thu, 18 May 2023 22:56:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=9D=83=E9=99=90=E8=BF=87?= =?UTF-8?q?=E6=BB=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../console/aspect/PermissionAspect.java | 127 ++++++++++++++++++ .../console/aspect/annotation/Permission.java | 17 +++ .../console/beans/RolePermUpdateEvent.java | 22 +++ .../kafka/console/cache/RolePermCache.java | 91 +++++++++++++ .../console/controller/AclAuthController.java | 11 ++ .../console/controller/AclUserController.java | 6 + .../controller/ClientQuotaController.java | 4 + .../console/controller/ClusterController.java | 4 + .../console/controller/ConfigController.java | 10 ++ .../controller/ConsumerController.java | 30 +++-- .../console/controller/MessageController.java | 7 + .../controller/OperationController.java | 14 +- .../console/controller/TopicController.java | 20 +-- .../controller/UserManageController.java | 12 +- .../xuxd/kafka/console/dao/init/DataInit.java | 11 +- .../GlobalExceptionHandler.java | 15 ++- .../exception/UnAuthorizedException.java | 12 ++ .../{interceptor => filter}/AuthFilter.java | 9 +- .../ContextSetFilter.java | 2 +- .../console/filter/CredentialsContext.java | 23 ++++ .../console/service/impl/AuthServiceImpl.java | 29 ++-- .../service/impl/UserManageServiceImpl.java | 11 +- src/main/resources/application.yml | 2 +- src/main/resources/db/data-h2.sql | 11 +- ui/src/utils/request.js | 6 + ui/src/views/message/MessageDetail.vue | 4 +- ui/src/views/message/MessageList.vue | 1 + ui/src/views/op/CurrentReassignments.vue | 6 +- ui/src/views/topic/TopicConfig.vue | 7 +- ui/src/views/user/User.vue | 2 +- 30 files changed, 463 insertions(+), 63 deletions(-) create mode 100644 src/main/java/com/xuxd/kafka/console/aspect/PermissionAspect.java create mode 100644 src/main/java/com/xuxd/kafka/console/aspect/annotation/Permission.java create mode 100644 src/main/java/com/xuxd/kafka/console/beans/RolePermUpdateEvent.java create mode 100644 src/main/java/com/xuxd/kafka/console/cache/RolePermCache.java rename src/main/java/com/xuxd/kafka/console/{interceptor => exception}/GlobalExceptionHandler.java (60%) create mode 100644 src/main/java/com/xuxd/kafka/console/exception/UnAuthorizedException.java rename src/main/java/com/xuxd/kafka/console/{interceptor => filter}/AuthFilter.java (89%) rename src/main/java/com/xuxd/kafka/console/{interceptor => filter}/ContextSetFilter.java (98%) create mode 100644 src/main/java/com/xuxd/kafka/console/filter/CredentialsContext.java diff --git a/src/main/java/com/xuxd/kafka/console/aspect/PermissionAspect.java b/src/main/java/com/xuxd/kafka/console/aspect/PermissionAspect.java new file mode 100644 index 0000000..29dfafd --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/aspect/PermissionAspect.java @@ -0,0 +1,127 @@ +package com.xuxd.kafka.console.aspect; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.xuxd.kafka.console.aspect.annotation.Permission; +import com.xuxd.kafka.console.beans.Credentials; +import com.xuxd.kafka.console.beans.dos.SysUserDO; +import com.xuxd.kafka.console.cache.RolePermCache; +import com.xuxd.kafka.console.config.AuthConfig; +import com.xuxd.kafka.console.dao.SysPermissionMapper; +import com.xuxd.kafka.console.dao.SysRoleMapper; +import com.xuxd.kafka.console.dao.SysUserMapper; +import com.xuxd.kafka.console.exception.UnAuthorizedException; +import com.xuxd.kafka.console.filter.CredentialsContext; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: xuxd + * @date: 2023/5/17 22:32 + **/ +@Slf4j +@Order(1) +@Aspect +@Component +public class PermissionAspect { + + + private Map> permMap = new HashMap<>(); + + private final AuthConfig authConfig; + + private final SysUserMapper userMapper; + + private final SysRoleMapper roleMapper; + + private final SysPermissionMapper permissionMapper; + + private final RolePermCache rolePermCache; + + public PermissionAspect(AuthConfig authConfig, + SysUserMapper userMapper, + SysRoleMapper roleMapper, + SysPermissionMapper permissionMapper, + RolePermCache rolePermCache) { + this.authConfig = authConfig; + this.userMapper = userMapper; + this.roleMapper = roleMapper; + this.permissionMapper = permissionMapper; + this.rolePermCache = rolePermCache; + } + + @Pointcut("@annotation(com.xuxd.kafka.console.aspect.annotation.Permission)") + private void pointcut() { + + } + + @Before(value = "pointcut()") + public void before(JoinPoint joinPoint) { + if (!authConfig.isEnable()) { + return; + } + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + Permission permission = method.getAnnotation(Permission.class); + if (permission == null) { + return; + } + String[] value = permission.value(); + if (value == null || value.length == 0) { + return; + } + String name = method.getName() + "@" + method.hashCode(); + + Map> pm = checkPermMap(name, value); + + Set allowPermSet = pm.get(name); + if (allowPermSet == null) { + log.error("解析权限出现意外啦!!!"); + return; + } + + Credentials credentials = CredentialsContext.get(); + if (credentials == null || credentials.isInvalid()) { + throw new UnAuthorizedException("credentials is invalid"); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", credentials.getUsername()); + SysUserDO userDO = userMapper.selectOne(queryWrapper); + if (userDO == null) { + throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet); + } + + String roleIds = userDO.getRoleIds(); + List roleIdList = Arrays.stream(roleIds.split(",")).map(String::trim).filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList()); + for (Long roleId : roleIdList) { + Set permSet = rolePermCache.getRolePermCache().getOrDefault(roleId, Collections.emptySet()); + for (String p : allowPermSet) { + if (permSet.contains(p)) { + return; + } + } + } + throw new UnAuthorizedException(credentials.getUsername() + ":" + allowPermSet); + } + + private Map> checkPermMap(String methodName, String[] value) { + if (!permMap.containsKey(methodName)) { + Map> map = new HashMap<>(permMap); + map.put(methodName, new HashSet<>(Arrays.asList(value))); + permMap = map; + return map; + } + return permMap; + } + +} diff --git a/src/main/java/com/xuxd/kafka/console/aspect/annotation/Permission.java b/src/main/java/com/xuxd/kafka/console/aspect/annotation/Permission.java new file mode 100644 index 0000000..344634c --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/aspect/annotation/Permission.java @@ -0,0 +1,17 @@ +package com.xuxd.kafka.console.aspect.annotation; + +import java.lang.annotation.*; + +/** + * 权限注解,开启认证的时候拥有该权限的用户才能访问对应接口. + * + * @author: xuxd + * @date: 2023/5/17 22:30 + **/ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Permission { + + String[] value() default {}; +} diff --git a/src/main/java/com/xuxd/kafka/console/beans/RolePermUpdateEvent.java b/src/main/java/com/xuxd/kafka/console/beans/RolePermUpdateEvent.java new file mode 100644 index 0000000..8c6d64e --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/beans/RolePermUpdateEvent.java @@ -0,0 +1,22 @@ +package com.xuxd.kafka.console.beans; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.context.ApplicationEvent; + +/** + * @author: xuxd + * @date: 2023/5/18 15:49 + **/ +@ToString +public class RolePermUpdateEvent extends ApplicationEvent { + + @Getter + @Setter + private boolean reload = false; + + public RolePermUpdateEvent(Object source) { + super(source); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/cache/RolePermCache.java b/src/main/java/com/xuxd/kafka/console/cache/RolePermCache.java new file mode 100644 index 0000000..2ea8a2c --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/cache/RolePermCache.java @@ -0,0 +1,91 @@ +package com.xuxd.kafka.console.cache; + +import com.xuxd.kafka.console.beans.RolePermUpdateEvent; +import com.xuxd.kafka.console.beans.dos.SysPermissionDO; +import com.xuxd.kafka.console.beans.dos.SysRoleDO; +import com.xuxd.kafka.console.dao.SysPermissionMapper; +import com.xuxd.kafka.console.dao.SysRoleMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author: xuxd + * @date: 2023/5/18 15:47 + **/ +@DependsOn("dataInit") +@Slf4j +@Component +public class RolePermCache implements ApplicationListener, SmartInitializingSingleton { + + private Map permCache = new HashMap<>(); + + private Map> rolePermCache = new HashMap<>(); + + private final SysPermissionMapper permissionMapper; + + private final SysRoleMapper roleMapper; + + public RolePermCache(SysPermissionMapper permissionMapper, SysRoleMapper roleMapper) { + this.permissionMapper = permissionMapper; + this.roleMapper = roleMapper; + } + + @Override + public void onApplicationEvent(RolePermUpdateEvent event) { + log.info("更新角色权限信息:{}", event); + if (event.isReload()) { + this.loadPermCache(); + } + refresh(); + } + + public Map getPermCache() { + return permCache; + } + + public Map> getRolePermCache() { + return rolePermCache; + } + + private void refresh() { + List roleDOS = roleMapper.selectList(null); + Map> tmp = new HashMap<>(); + for (SysRoleDO roleDO : roleDOS) { + String permissionIds = roleDO.getPermissionIds(); + if (StringUtils.isEmpty(permissionIds)) { + continue; + } + List list = Arrays.stream(permissionIds.split(",")).map(String::trim).filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList()); + Set permSet = tmp.getOrDefault(roleDO.getId(), new HashSet<>()); + for (Long permId : list) { + SysPermissionDO permissionDO = permCache.get(permId); + if (permissionDO != null) { + permSet.add(permissionDO.getPermission()); + } + } + tmp.put(roleDO.getId(), permSet); + } + rolePermCache = tmp; + } + + private void loadPermCache() { + List roleDOS = permissionMapper.selectList(null); + Map map = roleDOS.stream().collect(Collectors.toMap(SysPermissionDO::getId, Function.identity(), (e1, e2) -> e1)); + permCache = map; + } + + + @Override + public void afterSingletonsInstantiated() { + this.loadPermCache(); + this.refresh(); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/controller/AclAuthController.java b/src/main/java/com/xuxd/kafka/console/controller/AclAuthController.java index 0c9ee4f..8bfdbbb 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/AclAuthController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/AclAuthController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.AclEntry; import com.xuxd.kafka.console.beans.dto.AddAuthDTO; import com.xuxd.kafka.console.beans.dto.ConsumerAuthDTO; @@ -28,6 +29,7 @@ public class AclAuthController { @Autowired private AclService aclService; + @Permission({"acl:authority:detail", "acl:sasl-scram:detail"}) @PostMapping("/detail") public Object getAclDetailList(@RequestBody QueryAclDTO param) { return aclService.getAclDetailList(param.toEntry()); @@ -38,11 +40,13 @@ public class AclAuthController { return aclService.getOperationList(); } + @Permission("acl:authority") @PostMapping("/list") public Object getAclList(@RequestBody QueryAclDTO param) { return aclService.getAclList(param.toEntry()); } + @Permission({"acl:authority:add-principal", "acl:authority:add", "acl:sasl-scram:add-auth"}) @PostMapping public Object addAcl(@RequestBody AddAuthDTO param) { return aclService.addAcl(param.toAclEntry()); @@ -54,6 +58,7 @@ public class AclAuthController { * @param param entry.topic && entry.username must. * @return */ + @Permission({"acl:authority:producer", "acl:sasl-scram:producer"}) @PostMapping("/producer") public Object addProducerAcl(@RequestBody ProducerAuthDTO param) { @@ -66,6 +71,7 @@ public class AclAuthController { * @param param entry.topic && entry.groupId entry.username must. * @return */ + @Permission({"acl:authority:consumer", "acl:sasl-scram:consumer"}) @PostMapping("/consumer") public Object addConsumerAcl(@RequestBody ConsumerAuthDTO param) { @@ -78,6 +84,7 @@ public class AclAuthController { * @param entry entry * @return */ + @Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @DeleteMapping public Object deleteAclByUser(@RequestBody AclEntry entry) { return aclService.deleteAcl(entry); @@ -89,6 +96,7 @@ public class AclAuthController { * @param param entry.username * @return */ + @Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @DeleteMapping("/user") public Object deleteAclByUser(@RequestBody DeleteAclDTO param) { return aclService.deleteUserAcl(param.toUserEntry()); @@ -100,6 +108,7 @@ public class AclAuthController { * @param param entry.topic && entry.username must. * @return */ + @Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @DeleteMapping("/producer") public Object deleteProducerAcl(@RequestBody ProducerAuthDTO param) { @@ -112,6 +121,7 @@ public class AclAuthController { * @param param entry.topic && entry.groupId entry.username must. * @return */ + @Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @DeleteMapping("/consumer") public Object deleteConsumerAcl(@RequestBody ConsumerAuthDTO param) { @@ -124,6 +134,7 @@ public class AclAuthController { * @param param acl principal. * @return true or false. */ + @Permission({"acl:authority:clean", "acl:sasl-scram:pure"}) @DeleteMapping("/clear") public Object clearAcl(@RequestBody DeleteAclDTO param) { return aclService.clearAcl(param.toUserEntry()); diff --git a/src/main/java/com/xuxd/kafka/console/controller/AclUserController.java b/src/main/java/com/xuxd/kafka/console/controller/AclUserController.java index 7570287..932d68a 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/AclUserController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/AclUserController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.AclEntry; import com.xuxd.kafka.console.beans.AclUser; import com.xuxd.kafka.console.service.AclService; @@ -26,27 +27,32 @@ public class AclUserController { @Autowired private AclService aclService; + @Permission("acl:sasl-scram") @GetMapping public Object getUserList() { return aclService.getUserList(); } + @Permission({"acl:sasl-scram:add-update", "acl:sasl-scram:add-auth"}) @PostMapping public Object addOrUpdateUser(@RequestBody AclUser user) { return aclService.addOrUpdateUser(user.getUsername(), user.getPassword()); } + @Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"}) @DeleteMapping public Object deleteUser(@RequestBody AclUser user) { return aclService.deleteUser(user.getUsername()); } + @Permission({"acl:sasl-scram:del", "acl:sasl-scram:pure"}) @DeleteMapping("/auth") public Object deleteUserAndAuth(@RequestBody AclUser user) { return aclService.deleteUserAndAuth(user.getUsername()); } + @Permission("acl:sasl-scram:detail") @GetMapping("/detail") public Object getUserDetail(@RequestParam String username) { return aclService.getUserDetail(username); diff --git a/src/main/java/com/xuxd/kafka/console/controller/ClientQuotaController.java b/src/main/java/com/xuxd/kafka/console/controller/ClientQuotaController.java index ad6c921..e11da6e 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/ClientQuotaController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/ClientQuotaController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.dto.AlterClientQuotaDTO; import com.xuxd.kafka.console.beans.dto.QueryClientQuotaDTO; @@ -21,11 +22,13 @@ public class ClientQuotaController { this.clientQuotaService = clientQuotaService; } + @Permission({"quota:user", "quota:client", "quota:user-client"}) @PostMapping("/list") public Object getClientQuotaConfigs(@RequestBody QueryClientQuotaDTO request) { return clientQuotaService.getClientQuotaConfigs(request.getTypes(), request.getNames()); } + @Permission({"quota:user:add", "quota:client:add", "quota:user-client:add", "quota:edit"}) @PostMapping public Object alterClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) { if (request.getTypes().size() != 2) { @@ -38,6 +41,7 @@ public class ClientQuotaController { return clientQuotaService.alterClientQuotaConfigs(request); } + @Permission("quota:del") @DeleteMapping public Object deleteClientQuotaConfigs(@RequestBody AlterClientQuotaDTO request) { if (request.getTypes().size() != 2) { diff --git a/src/main/java/com/xuxd/kafka/console/controller/ClusterController.java b/src/main/java/com/xuxd/kafka/console/controller/ClusterController.java index d9c67bd..14e0a39 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/ClusterController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/ClusterController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.dto.ClusterInfoDTO; import com.xuxd.kafka.console.service.ClusterService; import org.springframework.beans.factory.annotation.Autowired; @@ -34,16 +35,19 @@ public class ClusterController { return clusterService.getClusterInfoList(); } + @Permission("op:cluster-switch:add") @PostMapping("/info") public Object addClusterInfo(@RequestBody ClusterInfoDTO dto) { return clusterService.addClusterInfo(dto.to()); } + @Permission("op:cluster-switch:del") @DeleteMapping("/info") public Object deleteClusterInfo(@RequestBody ClusterInfoDTO dto) { return clusterService.deleteClusterInfo(dto.getId()); } + @Permission("op:cluster-switch:edit") @PutMapping("/info") public Object updateClusterInfo(@RequestBody ClusterInfoDTO dto) { return clusterService.updateClusterInfo(dto.to()); diff --git a/src/main/java/com/xuxd/kafka/console/controller/ConfigController.java b/src/main/java/com/xuxd/kafka/console/controller/ConfigController.java index c7104dd..ffd37b9 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/ConfigController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/ConfigController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.dto.AlterConfigDTO; import com.xuxd.kafka.console.beans.enums.AlterType; @@ -41,46 +42,55 @@ public class ConfigController { return ResponseData.create().data(configMap).success(); } + @Permission("topic:property-config") @GetMapping("/topic") public Object getTopicConfig(String topic) { return configService.getTopicConfig(topic); } + @Permission("topic:property-config:edit") @PostMapping("/topic") public Object setTopicConfig(@RequestBody AlterConfigDTO dto) { return configService.alterTopicConfig(dto.getEntity(), dto.to(), AlterType.SET); } + @Permission("topic:property-config:del") @DeleteMapping("/topic") public Object deleteTopicConfig(@RequestBody AlterConfigDTO dto) { return configService.alterTopicConfig(dto.getEntity(), dto.to(), AlterType.DELETE); } + @Permission("cluster:property-config") @GetMapping("/broker") public Object getBrokerConfig(String brokerId) { return configService.getBrokerConfig(brokerId); } + @Permission("cluster:edit") @PostMapping("/broker") public Object setBrokerConfig(@RequestBody AlterConfigDTO dto) { return configService.alterBrokerConfig(dto.getEntity(), dto.to(), AlterType.SET); } + @Permission("cluster:edit") @DeleteMapping("/broker") public Object deleteBrokerConfig(@RequestBody AlterConfigDTO dto) { return configService.alterBrokerConfig(dto.getEntity(), dto.to(), AlterType.DELETE); } + @Permission("cluster:log-config") @GetMapping("/broker/logger") public Object getBrokerLoggerConfig(String brokerId) { return configService.getBrokerLoggerConfig(brokerId); } + @Permission("cluster:edit") @PostMapping("/broker/logger") public Object setBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) { return configService.alterBrokerLoggerConfig(dto.getEntity(), dto.to(), AlterType.SET); } + @Permission("cluster:edit") @DeleteMapping("/broker/logger") public Object deleteBrokerLoggerConfig(@RequestBody AlterConfigDTO dto) { return configService.alterBrokerLoggerConfig(dto.getEntity(), dto.to(), AlterType.DELETE); diff --git a/src/main/java/com/xuxd/kafka/console/controller/ConsumerController.java b/src/main/java/com/xuxd/kafka/console/controller/ConsumerController.java index 8418abd..c5a7320 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/ConsumerController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/ConsumerController.java @@ -1,28 +1,20 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.dto.AddSubscriptionDTO; import com.xuxd.kafka.console.beans.dto.QueryConsumerGroupDTO; import com.xuxd.kafka.console.beans.dto.ResetOffsetDTO; import com.xuxd.kafka.console.service.ConsumerService; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.kafka.clients.consumer.OffsetResetStrategy; import org.apache.kafka.common.ConsumerGroupState; import org.apache.kafka.common.TopicPartition; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.*; /** * kafka-console-ui. @@ -51,26 +43,34 @@ public class ConsumerController { return consumerService.getConsumerGroupList(groupIdList, stateSet); } + @Permission("group:del") @DeleteMapping("/group") public Object deleteConsumerGroup(@RequestParam String groupId) { return consumerService.deleteConsumerGroup(groupId); } + @Permission("group:client") @GetMapping("/member") public Object getConsumerMembers(@RequestParam String groupId) { return consumerService.getConsumerMembers(groupId); } + @Permission("group:consumer-detail") @GetMapping("/detail") public Object getConsumerDetail(@RequestParam String groupId) { return consumerService.getConsumerDetail(groupId); } + @Permission("group:add") @PostMapping("/subscription") public Object addSubscription(@RequestBody AddSubscriptionDTO subscriptionDTO) { return consumerService.addSubscription(subscriptionDTO.getGroupId(), subscriptionDTO.getTopic()); } + @Permission({"group:consumer-detail:min", + "group:consumer-detail:last", + "group:consumer-detail:timestamp", + "group:consumer-detail:any"}) @PostMapping("/reset/offset") public Object restOffset(@RequestBody ResetOffsetDTO offsetDTO) { ResponseData res = ResponseData.create().failed("unknown"); @@ -78,7 +78,7 @@ public class ConsumerController { case ResetOffsetDTO.Level.TOPIC: switch (offsetDTO.getType()) { case ResetOffsetDTO.Type - .EARLIEST: + .EARLIEST: res = consumerService.resetOffsetToEndpoint(offsetDTO.getGroupId(), offsetDTO.getTopic(), OffsetResetStrategy.EARLIEST); break; case ResetOffsetDTO.Type.LATEST: @@ -94,7 +94,7 @@ public class ConsumerController { case ResetOffsetDTO.Level.PARTITION: switch (offsetDTO.getType()) { case ResetOffsetDTO.Type - .SPECIAL: + .SPECIAL: res = consumerService.resetPartitionToTargetOffset(offsetDTO.getGroupId(), new TopicPartition(offsetDTO.getTopic(), offsetDTO.getPartition()), offsetDTO.getOffset()); break; default: @@ -118,11 +118,13 @@ public class ConsumerController { return consumerService.getSubscribeTopicList(groupId); } + @Permission({"topic:consumer-detail"}) @GetMapping("/topic/subscribed") public Object getTopicSubscribedByGroups(@RequestParam String topic) { return consumerService.getTopicSubscribedByGroups(topic); } + @Permission("group:offset-partition") @GetMapping("/offset/partition") public Object getOffsetPartition(@RequestParam String groupId) { return consumerService.getOffsetPartition(groupId); diff --git a/src/main/java/com/xuxd/kafka/console/controller/MessageController.java b/src/main/java/com/xuxd/kafka/console/controller/MessageController.java index 154f4bb..29f3670 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/MessageController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/MessageController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.QueryMessage; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.SendMessage; @@ -24,16 +25,19 @@ public class MessageController { @Autowired private MessageService messageService; + @Permission("message:search-time") @PostMapping("/search/time") public Object searchByTime(@RequestBody QueryMessageDTO dto) { return messageService.searchByTime(dto.toQueryMessage()); } + @Permission("message:search-offset") @PostMapping("/search/offset") public Object searchByOffset(@RequestBody QueryMessageDTO dto) { return messageService.searchByOffset(dto.toQueryMessage()); } + @Permission("message:detail") @PostMapping("/search/detail") public Object searchDetail(@RequestBody QueryMessageDTO dto) { return messageService.searchDetail(dto.toQueryMessage()); @@ -44,16 +48,19 @@ public class MessageController { return messageService.deserializerList(); } + @Permission("message:send") @PostMapping("/send") public Object send(@RequestBody SendMessage message) { return messageService.send(message); } + @Permission("message:resend") @PostMapping("/resend") public Object resend(@RequestBody SendMessage message) { return messageService.resend(message); } + @Permission("message:del") @DeleteMapping public Object delete(@RequestBody List messages) { if (CollectionUtils.isEmpty(messages)) { diff --git a/src/main/java/com/xuxd/kafka/console/controller/OperationController.java b/src/main/java/com/xuxd/kafka/console/controller/OperationController.java index cf5e509..4daf6bc 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/OperationController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/OperationController.java @@ -1,5 +1,6 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.TopicPartition; import com.xuxd.kafka.console.beans.dto.BrokerThrottleDTO; import com.xuxd.kafka.console.beans.dto.ProposedAssignmentDTO; @@ -8,13 +9,7 @@ import com.xuxd.kafka.console.beans.dto.SyncDataDTO; import com.xuxd.kafka.console.service.OperationService; import org.apache.kafka.clients.admin.AdminClientConfig; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** * kafka-console-ui. @@ -51,26 +46,31 @@ public class OperationController { return operationService.deleteAlignmentById(id); } + @Permission({"topic:partition-detail:preferred", "op:replication-preferred"}) @PostMapping("/replication/preferred") public Object electPreferredLeader(@RequestBody ReplicationDTO dto) { return operationService.electPreferredLeader(dto.getTopic(), dto.getPartition()); } + @Permission("op:config-throttle") @PostMapping("/broker/throttle") public Object configThrottle(@RequestBody BrokerThrottleDTO dto) { return operationService.configThrottle(dto.getBrokerList(), dto.getUnit().toKb(dto.getThrottle())); } + @Permission("op:remove-throttle") @DeleteMapping("/broker/throttle") public Object removeThrottle(@RequestBody BrokerThrottleDTO dto) { return operationService.removeThrottle(dto.getBrokerList()); } + @Permission("op:replication-update-detail") @GetMapping("/replication/reassignments") public Object currentReassignments() { return operationService.currentReassignments(); } + @Permission("op:replication-update-detail:cancel") @DeleteMapping("/replication/reassignments") public Object cancelReassignment(@RequestBody TopicPartition partition) { return operationService.cancelReassignment(new org.apache.kafka.common.TopicPartition(partition.getTopic(), partition.getPartition())); diff --git a/src/main/java/com/xuxd/kafka/console/controller/TopicController.java b/src/main/java/com/xuxd/kafka/console/controller/TopicController.java index c5b083b..318630a 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/TopicController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/TopicController.java @@ -1,23 +1,19 @@ package com.xuxd.kafka.console.controller; +import com.xuxd.kafka.console.aspect.annotation.Permission; import com.xuxd.kafka.console.beans.ReplicaAssignment; import com.xuxd.kafka.console.beans.dto.AddPartitionDTO; import com.xuxd.kafka.console.beans.dto.NewTopicDTO; import com.xuxd.kafka.console.beans.dto.TopicThrottleDTO; import com.xuxd.kafka.console.beans.enums.TopicType; import com.xuxd.kafka.console.service.TopicService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; /** * kafka-console-ui. @@ -37,26 +33,31 @@ public class TopicController { return topicService.getTopicNameList(false); } + @Permission("topic:load") @GetMapping("/list") public Object getTopicList(@RequestParam(required = false) String topic, @RequestParam String type) { return topicService.getTopicList(topic, TopicType.valueOf(type.toUpperCase())); } + @Permission({"topic:batch-del", "topic:del"}) @DeleteMapping public Object deleteTopic(@RequestBody List topics) { return topicService.deleteTopics(topics); } + @Permission("topic:partition-detail") @GetMapping("/partition") public Object getTopicPartitionInfo(@RequestParam String topic) { return topicService.getTopicPartitionInfo(topic.trim()); } + @Permission("topic:add") @PostMapping("/new") public Object createNewTopic(@RequestBody NewTopicDTO topicDTO) { return topicService.createTopic(topicDTO.toNewTopic()); } + @Permission("topic:partition-add") @PostMapping("/partition/new") public Object addPartition(@RequestBody AddPartitionDTO partitionDTO) { String topic = partitionDTO.getTopic().trim(); @@ -79,16 +80,19 @@ public class TopicController { return topicService.getCurrentReplicaAssignment(topic); } + @Permission({"topic:replication-modify", "op:replication-reassign"}) @PostMapping("/replica/assignment") public Object updateReplicaAssignment(@RequestBody ReplicaAssignment assignment) { return topicService.updateReplicaAssignment(assignment); } + @Permission("topic:replication-sync-throttle") @PostMapping("/replica/throttle") public Object configThrottle(@RequestBody TopicThrottleDTO dto) { return topicService.configThrottle(dto.getTopic(), dto.getPartitions(), dto.getOperation()); } + @Permission("topic:send-count") @GetMapping("/send/stats") public Object sendStats(@RequestParam String topic) { return topicService.sendStats(topic); diff --git a/src/main/java/com/xuxd/kafka/console/controller/UserManageController.java b/src/main/java/com/xuxd/kafka/console/controller/UserManageController.java index 7c81ead..6af2def 100644 --- a/src/main/java/com/xuxd/kafka/console/controller/UserManageController.java +++ b/src/main/java/com/xuxd/kafka/console/controller/UserManageController.java @@ -1,6 +1,7 @@ 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.beans.Credentials; import com.xuxd.kafka.console.beans.dto.SysPermissionDTO; import com.xuxd.kafka.console.beans.dto.SysRoleDTO; @@ -24,12 +25,14 @@ public class UserManageController { this.userManageService = userManageService; } + @Permission({"user-manage:user:add", "user-manage:user:change-role", "user-manage:user:reset-pass"}) @ControllerLog("新增/更新用户") @PostMapping("/user") public Object addOrUpdateUser(@RequestBody SysUserDTO userDTO) { return userManageService.addOrUpdateUser(userDTO); } + @Permission("user-manage:role:save") @ControllerLog("新增/更新角色") @PostMapping("/role") public Object addOrUpdateRole(@RequestBody SysRoleDTO roleDTO) { @@ -42,43 +45,50 @@ public class UserManageController { return userManageService.addPermission(permissionDTO); } + @Permission("user-manage:role:save") @ControllerLog("更新角色") @PutMapping("/role") public Object updateRole(@RequestBody SysRoleDTO roleDTO) { return userManageService.updateRole(roleDTO); } + @Permission({"user-manage:role"}) @GetMapping("/role") public Object selectRole() { return userManageService.selectRole(); } + @Permission({"user-manage:permission"}) @GetMapping("/permission") public Object selectPermission() { return userManageService.selectPermission(); } + @Permission({"user-manage:user"}) @GetMapping("/user") public Object selectUser() { return userManageService.selectUser(); } + @Permission("user-manage:role:del") @ControllerLog("删除角色") @DeleteMapping("/role") public Object deleteRole(@RequestParam Long id) { return userManageService.deleteRole(id); } + @Permission("user-manage:user:del") @ControllerLog("删除用户") @DeleteMapping("/user") public Object deleteUser(@RequestParam Long id) { return userManageService.deleteUser(id); } + @Permission("user-manage:setting") @ControllerLog("更新密码") @PostMapping("/user/password") public Object updatePassword(@RequestBody SysUserDTO userDTO, HttpServletRequest request) { - Credentials credentials = (Credentials)request.getAttribute("credentials"); + Credentials credentials = (Credentials) request.getAttribute("credentials"); if (credentials != null && !credentials.isInvalid()) { userDTO.setUsername(credentials.getUsername()); } diff --git a/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java b/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java index a3e515e..71a9e0a 100644 --- a/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java +++ b/src/main/java/com/xuxd/kafka/console/dao/init/DataInit.java @@ -1,11 +1,13 @@ package com.xuxd.kafka.console.dao.init; +import com.xuxd.kafka.console.beans.RolePermUpdateEvent; import com.xuxd.kafka.console.config.AuthConfig; import com.xuxd.kafka.console.dao.SysPermissionMapper; import com.xuxd.kafka.console.dao.SysRoleMapper; import com.xuxd.kafka.console.dao.SysUserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import javax.sql.DataSource; @@ -33,17 +35,21 @@ public class DataInit implements SmartInitializingSingleton { private final SqlParse sqlParse; + private final ApplicationEventPublisher publisher; + public DataInit(AuthConfig authConfig, SysUserMapper userMapper, SysRoleMapper roleMapper, SysPermissionMapper permissionMapper, - DataSource dataSource) { + DataSource dataSource, + ApplicationEventPublisher publisher) { this.authConfig = authConfig; this.userMapper = userMapper; this.roleMapper = roleMapper; this.permissionMapper = permissionMapper; this.dataSource = dataSource; + this.publisher = publisher; this.sqlParse = new SqlParse(); } @@ -69,6 +75,9 @@ public class DataInit implements SmartInitializingSingleton { if (permCount == null || permCount == 0) { initData(connection, SqlParse.PERM_TABLE); } + RolePermUpdateEvent event = new RolePermUpdateEvent(this); + event.setReload(true); + publisher.publishEvent(event); } catch (SQLException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/xuxd/kafka/console/interceptor/GlobalExceptionHandler.java b/src/main/java/com/xuxd/kafka/console/exception/GlobalExceptionHandler.java similarity index 60% rename from src/main/java/com/xuxd/kafka/console/interceptor/GlobalExceptionHandler.java rename to src/main/java/com/xuxd/kafka/console/exception/GlobalExceptionHandler.java index 7715f45..9090ce2 100644 --- a/src/main/java/com/xuxd/kafka/console/interceptor/GlobalExceptionHandler.java +++ b/src/main/java/com/xuxd/kafka/console/exception/GlobalExceptionHandler.java @@ -1,11 +1,14 @@ -package com.xuxd.kafka.console.interceptor; +package com.xuxd.kafka.console.exception; import com.xuxd.kafka.console.beans.ResponseData; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import javax.servlet.http.HttpServletRequest; /** * kafka-console-ui. @@ -17,6 +20,14 @@ import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice(basePackages = "com.xuxd.kafka.console.controller") public class GlobalExceptionHandler { + @ResponseStatus(code = HttpStatus.FORBIDDEN) + @ExceptionHandler(value = UnAuthorizedException.class) + @ResponseBody + public Object unAuthorizedExceptionHandler(HttpServletRequest req, Exception ex) throws Exception { + log.error("unAuthorized: {}", ex.getMessage()); + return ResponseData.create().failed("UnAuthorized: " + ex.getMessage()); + } + @ExceptionHandler(value = Exception.class) @ResponseBody public Object exceptionHandler(HttpServletRequest req, Exception ex) throws Exception { diff --git a/src/main/java/com/xuxd/kafka/console/exception/UnAuthorizedException.java b/src/main/java/com/xuxd/kafka/console/exception/UnAuthorizedException.java new file mode 100644 index 0000000..c29cd6d --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/exception/UnAuthorizedException.java @@ -0,0 +1,12 @@ +package com.xuxd.kafka.console.exception; + +/** + * @author: xuxd + * @date: 2023/5/17 23:08 + **/ +public class UnAuthorizedException extends RuntimeException{ + + public UnAuthorizedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/interceptor/AuthFilter.java b/src/main/java/com/xuxd/kafka/console/filter/AuthFilter.java similarity index 89% rename from src/main/java/com/xuxd/kafka/console/interceptor/AuthFilter.java rename to src/main/java/com/xuxd/kafka/console/filter/AuthFilter.java index aea95f1..6137eb3 100644 --- a/src/main/java/com/xuxd/kafka/console/interceptor/AuthFilter.java +++ b/src/main/java/com/xuxd/kafka/console/filter/AuthFilter.java @@ -1,4 +1,4 @@ -package com.xuxd.kafka.console.interceptor; +package com.xuxd.kafka.console.filter; import com.xuxd.kafka.console.beans.Credentials; import com.xuxd.kafka.console.config.AuthConfig; @@ -60,6 +60,11 @@ public class AuthFilter implements Filter { } request.setAttribute("credentials", credentials); - filterChain.doFilter(servletRequest, servletResponse); + try { + CredentialsContext.set(credentials); + filterChain.doFilter(servletRequest, servletResponse); + }finally { + CredentialsContext.remove(); + } } } diff --git a/src/main/java/com/xuxd/kafka/console/interceptor/ContextSetFilter.java b/src/main/java/com/xuxd/kafka/console/filter/ContextSetFilter.java similarity index 98% rename from src/main/java/com/xuxd/kafka/console/interceptor/ContextSetFilter.java rename to src/main/java/com/xuxd/kafka/console/filter/ContextSetFilter.java index d336f9c..a43fc1d 100644 --- a/src/main/java/com/xuxd/kafka/console/interceptor/ContextSetFilter.java +++ b/src/main/java/com/xuxd/kafka/console/filter/ContextSetFilter.java @@ -1,4 +1,4 @@ -package com.xuxd.kafka.console.interceptor; +package com.xuxd.kafka.console.filter; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.beans.dos.ClusterInfoDO; diff --git a/src/main/java/com/xuxd/kafka/console/filter/CredentialsContext.java b/src/main/java/com/xuxd/kafka/console/filter/CredentialsContext.java new file mode 100644 index 0000000..4ec9a6c --- /dev/null +++ b/src/main/java/com/xuxd/kafka/console/filter/CredentialsContext.java @@ -0,0 +1,23 @@ +package com.xuxd.kafka.console.filter; + +import com.xuxd.kafka.console.beans.Credentials; + +/** + * @author: xuxd + * @date: 2023/5/17 23:02 + **/ +public class CredentialsContext { + private static final ThreadLocal CREDENTIALS = new ThreadLocal<>(); + + public static void set(Credentials credentials) { + CREDENTIALS.set(credentials); + } + + public static Credentials get() { + return CREDENTIALS.get(); + } + + public static void remove() { + CREDENTIALS.remove(); + } +} diff --git a/src/main/java/com/xuxd/kafka/console/service/impl/AuthServiceImpl.java b/src/main/java/com/xuxd/kafka/console/service/impl/AuthServiceImpl.java index 783d1bf..5baafe4 100644 --- a/src/main/java/com/xuxd/kafka/console/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/xuxd/kafka/console/service/impl/AuthServiceImpl.java @@ -4,12 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.xuxd.kafka.console.beans.Credentials; import com.xuxd.kafka.console.beans.LoginResult; import com.xuxd.kafka.console.beans.ResponseData; -import com.xuxd.kafka.console.beans.dos.SysPermissionDO; import com.xuxd.kafka.console.beans.dos.SysRoleDO; import com.xuxd.kafka.console.beans.dos.SysUserDO; import com.xuxd.kafka.console.beans.dto.LoginUserDTO; +import com.xuxd.kafka.console.cache.RolePermCache; import com.xuxd.kafka.console.config.AuthConfig; -import com.xuxd.kafka.console.dao.SysPermissionMapper; import com.xuxd.kafka.console.dao.SysRoleMapper; import com.xuxd.kafka.console.dao.SysUserMapper; import com.xuxd.kafka.console.service.AuthService; @@ -17,11 +16,11 @@ import com.xuxd.kafka.console.utils.AuthUtil; import com.xuxd.kafka.console.utils.UUIDStrUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.stereotype.Service; -import java.util.*; -import java.util.function.Function; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; /** @@ -30,26 +29,24 @@ import java.util.stream.Collectors; **/ @Slf4j @Service -public class AuthServiceImpl implements AuthService, SmartInitializingSingleton { +public class AuthServiceImpl implements AuthService { private final SysUserMapper userMapper; private final SysRoleMapper roleMapper; - private final SysPermissionMapper permissionMapper; - private final AuthConfig authConfig; - private final Map permCache = new HashMap<>(); + private final RolePermCache rolePermCache; public AuthServiceImpl(SysUserMapper userMapper, SysRoleMapper roleMapper, - SysPermissionMapper permissionMapper, - AuthConfig authConfig) { + AuthConfig authConfig, + RolePermCache rolePermCache) { this.userMapper = userMapper; this.roleMapper = roleMapper; - this.permissionMapper = permissionMapper; this.authConfig = authConfig; + this.rolePermCache = rolePermCache; } @Override @@ -81,7 +78,7 @@ public class AuthServiceImpl implements AuthService, SmartInitializingSingleton List permIds = Arrays.stream(permissionIds.split(",")).map(String::trim). filter(StringUtils::isNotEmpty).map(Long::valueOf).collect(Collectors.toList()); permIds.forEach(id -> { - String permission = permCache.get(id).getPermission(); + String permission = rolePermCache.getPermCache().get(id).getPermission(); if (StringUtils.isNotEmpty(permission)) { permissions.add(permission); } else { @@ -96,10 +93,4 @@ public class AuthServiceImpl implements AuthService, SmartInitializingSingleton return ResponseData.create().data(loginResult).success(); } - @Override - public void afterSingletonsInstantiated() { - List roleDOS = permissionMapper.selectList(null); - Map map = roleDOS.stream().collect(Collectors.toMap(SysPermissionDO::getId, Function.identity(), (e1, e2) -> e1)); - permCache.putAll(map); - } } diff --git a/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java b/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java index be23f1d..8c6f483 100644 --- a/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java +++ b/src/main/java/com/xuxd/kafka/console/service/impl/UserManageServiceImpl.java @@ -2,6 +2,7 @@ package com.xuxd.kafka.console.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.xuxd.kafka.console.beans.ResponseData; +import com.xuxd.kafka.console.beans.RolePermUpdateEvent; import com.xuxd.kafka.console.beans.dos.SysPermissionDO; import com.xuxd.kafka.console.beans.dos.SysRoleDO; import com.xuxd.kafka.console.beans.dos.SysUserDO; @@ -20,6 +21,7 @@ import com.xuxd.kafka.console.utils.UUIDStrUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import java.util.*; @@ -40,12 +42,16 @@ public class UserManageServiceImpl implements UserManageService { private final SysPermissionMapper permissionMapper; + private final ApplicationEventPublisher publisher; + public UserManageServiceImpl(ObjectProvider userMapper, ObjectProvider roleMapper, - ObjectProvider permissionMapper) { + ObjectProvider permissionMapper, + ApplicationEventPublisher publisher) { this.userMapper = userMapper.getIfAvailable(); this.roleMapper = roleMapper.getIfAvailable(); this.permissionMapper = permissionMapper.getIfAvailable(); + this.publisher = publisher; } @Override @@ -62,6 +68,7 @@ public class UserManageServiceImpl implements UserManageService { } else { roleMapper.updateById(roleDO); } + publisher.publishEvent(new RolePermUpdateEvent(this)); return ResponseData.create().success(); } @@ -187,6 +194,7 @@ public class UserManageServiceImpl implements UserManageService { @Override public ResponseData updateRole(SysRoleDTO roleDTO) { roleMapper.updateById(roleDTO.toDO()); + publisher.publishEvent(new RolePermUpdateEvent(this)); return ResponseData.create().success(); } @@ -199,6 +207,7 @@ public class UserManageServiceImpl implements UserManageService { return ResponseData.create().failed("存在用户被分配为当前角色,不允许删除"); } roleMapper.deleteById(id); + publisher.publishEvent(new RolePermUpdateEvent(this)); return ResponseData.create().success(); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e372073..304feee 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -50,6 +50,6 @@ cron: # 权限认证设置,设置为true,需要先登录才能访问 auth: - enable: true + enable: false # 登录用户token的过期时间,单位:小时 expire-hours: 24 \ No newline at end of file diff --git a/src/main/resources/db/data-h2.sql b/src/main/resources/db/data-h2.sql index e350493..0cb8c1f 100644 --- a/src/main/resources/db/data-h2.sql +++ b/src/main/resources/db/data-h2.sql @@ -20,6 +20,8 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(30,'属 insert into t_sys_permission(id, name,type,parent_id,permission) values(31,'变更副本',1,21,'topic:replication-modify'); insert into t_sys_permission(id, name,type,parent_id,permission) values(32,'发送统计',1,21,'topic:send-count'); insert into t_sys_permission(id, name,type,parent_id,permission) values(33,'限流',1,21,'topic:replication-sync-throttle'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(34,'编辑属性配置',1,30,'topic:property-config:edit'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(35,'删除属性配置',1,30,'topic:property-config:del'); insert into t_sys_permission(id, name,type,parent_id,permission) values(41,'消费组',0,null,'group'); insert into t_sys_permission(id, name,type,parent_id,permission) values(42,'新增订阅',1,41,'group:add'); @@ -38,6 +40,8 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(62,'根 insert into t_sys_permission(id, name,type,parent_id,permission) values(63,'根据偏移查询',1,61,'message:search-offset'); insert into t_sys_permission(id, name,type,parent_id,permission) values(64,'在线发送',1,61,'message:send'); insert into t_sys_permission(id, name,type,parent_id,permission) values(65,'在线删除',1,61,'message:del'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(66,'消息详情',1,61,'message:detail'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(67,'重新发送',1,61,'message:resend'); insert into t_sys_permission(id, name,type,parent_id,permission) values(80,'限流',0,null,'quota'); insert into t_sys_permission(id, name,type,parent_id,permission) values(81,'用户',1,80,'quota:user'); @@ -90,13 +94,16 @@ insert into t_sys_permission(id, name,type,parent_id,permission) values(167,'解 insert into t_sys_permission(id, name,type,parent_id,permission) values(168,'首选副本作leader',1,160,'op:replication-preferred'); insert into t_sys_permission(id, name,type,parent_id,permission) values(169,'副本变更详情',1,160,'op:replication-update-detail'); insert into t_sys_permission(id, name,type,parent_id,permission) values(170,'副本重分配',1,160,'op:replication-reassign'); +insert into t_sys_permission(id, name,type,parent_id,permission) values(171,'取消副本重分配',1,169,'op:replication-update-detail:cancel'); -- t_sys_permission end-- -- t_sys_role start-- -insert into t_sys_role(id, role_name, description, permission_ids) VALUES (1,'超级管理员','超级管理员','12,13,14,22,23,24,25,26,27,28,29,30,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,81,82,83,84,85,86,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,170'); +insert into t_sys_role(id, role_name, description, permission_ids) VALUES (1,'超级管理员','超级管理员','12,13,14,22,23,24,25,26,27,28,29,30,34,35,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,66,67,81,82,83,84,85,86,87,88,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,141,142,143,144,145,146,147,148,149,150,161,162,163,164,165,166,167,168,169,171,170'); +insert into t_sys_role(id, role_name, description, permission_ids) VALUES (2,'普通管理员','普通管理员,不能更改用户信息','12,13,14,22,23,24,25,26,27,28,29,30,34,35,31,32,33,42,43,44,45,46,47,48,49,50,62,63,64,65,66,67,81,82,83,84,85,86,87,88,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,141,146,149,150,161,162,163,164,165,166,167,168,169,171,170'); -- insert into t_sys_role(id, role_name, description, permission_ids) VALUES (2,'访客','访客','12,13,22,26,29,32,44,45,50,62,63,81,83,85,141,146,149,150,161,163'); -- t_sys_role end-- -- t_sys_user start-- -insert into t_sys_user(id, username, password, salt, role_ids) VALUES (1,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' ); +insert into t_sys_user(id, username, password, salt, role_ids) VALUES (1,'super-admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','1' ); +insert into t_sys_user(id, username, password, salt, role_ids) VALUES (2,'admin','3a3e4d32-5247-321b-9efb-9cbf60b2bf6c','e6973cfc-7583-4baa-8802-65ded1268ab6','2' ); -- t_sys_user end-- \ No newline at end of file diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js index 6010991..859fecb 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -21,6 +21,12 @@ const errorHandler = (error) => { description: "请登录", }); Router.push({ path: "/login-page" }); + } else if (error.response.status == 403) { + // const data = error.response.data; + // notification.error({ + // message: error.response.status, + // description: data.msg, + // }); } else { const data = error.response.data; notification.error({ diff --git a/ui/src/views/message/MessageDetail.vue b/ui/src/views/message/MessageDetail.vue index eaa7094..ac202f0 100644 --- a/ui/src/views/message/MessageDetail.vue +++ b/ui/src/views/message/MessageDetail.vue @@ -116,7 +116,9 @@ cancel-text="取消" @confirm="resend" > - 重新发送 + + 重新发送 + diff --git a/ui/src/views/message/MessageList.vue b/ui/src/views/message/MessageList.vue index 1a11647..1756e15 100644 --- a/ui/src/views/message/MessageList.vue +++ b/ui/src/views/message/MessageList.vue @@ -17,6 +17,7 @@ href="javascript:;" class="operation-btn" @click="openDetailDialog(record)" + v-action:message:detail >消息详情 diff --git a/ui/src/views/op/CurrentReassignments.vue b/ui/src/views/op/CurrentReassignments.vue index 7b939d2..8bf587a 100644 --- a/ui/src/views/op/CurrentReassignments.vue +++ b/ui/src/views/op/CurrentReassignments.vue @@ -39,7 +39,11 @@ cancel-text="取消" @confirm="cancelReassignment(record)" > - 取消 diff --git a/ui/src/views/topic/TopicConfig.vue b/ui/src/views/topic/TopicConfig.vue index e23e78e..db08c9a 100644 --- a/ui/src/views/topic/TopicConfig.vue +++ b/ui/src/views/topic/TopicConfig.vue @@ -35,6 +35,7 @@ class="operation-btn" v-show="!record.readOnly" @click="openEditConfigDialog(record)" + v-action:topic:property-config:edit >编辑 - 删除 diff --git a/ui/src/views/user/User.vue b/ui/src/views/user/User.vue index b49e38a..6417f0d 100644 --- a/ui/src/views/user/User.vue +++ b/ui/src/views/user/User.vue @@ -45,7 +45,7 @@ bordered row-key="id" > -
+