From 34913542c0773455f7e50de861b5a854db9037ee Mon Sep 17 00:00:00 2001 From: bunny <1319900154@qq.com> Date: Sun, 1 Jun 2025 19:07:44 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E7=88=B6=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E7=BB=A7=E6=89=BF=E5=AD=90=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/aop/aspect/JobExecuteAspect.java | 2 +- .../listener/user/UserinfoUpdateListener.java | 8 +- .../core/utils/RouterServiceHelper.java | 89 +++++++++++++++++++ .../SecurityAuthenticationFailureHandler.java | 26 ------ .../SecurityAuthenticationSuccessHandler.java | 26 ------ .../UserLoginVoBuilderCacheService.java | 6 +- .../impl/RolePermissionServiceImpl.java | 5 +- .../system/impl/RouterServiceImpl.java | 5 +- .../system/impl/UserLoginServiceImpl.java | 22 ++--- .../java/impl/BuildPermissionApiTest.java | 79 ---------------- .../test/java/impl/ControllerScannerTest.java | 29 ------ 11 files changed, 113 insertions(+), 184 deletions(-) delete mode 100644 auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationFailureHandler.java delete mode 100644 auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationSuccessHandler.java rename auth-system/src/main/java/cn/bunny/services/{core/cache => security/service}/UserLoginVoBuilderCacheService.java (98%) delete mode 100644 auth-system/src/test/java/impl/BuildPermissionApiTest.java delete mode 100644 auth-system/src/test/java/impl/ControllerScannerTest.java diff --git a/auth-system/src/main/java/cn/bunny/services/aop/aspect/JobExecuteAspect.java b/auth-system/src/main/java/cn/bunny/services/aop/aspect/JobExecuteAspect.java index 1e3958d..c622f52 100644 --- a/auth-system/src/main/java/cn/bunny/services/aop/aspect/JobExecuteAspect.java +++ b/auth-system/src/main/java/cn/bunny/services/aop/aspect/JobExecuteAspect.java @@ -101,7 +101,7 @@ public class JobExecuteAspect { scheduleExecuteLogMapper.insert(executeLog); } - @Pointcut("execution(* cn.bunny.core.quartz.*.execute(..))") + @Pointcut("execution(* cn.bunny.services.quartz.*.execute(..))") public void pointCut() { } } diff --git a/auth-system/src/main/java/cn/bunny/services/core/event/listener/user/UserinfoUpdateListener.java b/auth-system/src/main/java/cn/bunny/services/core/event/listener/user/UserinfoUpdateListener.java index 8647e73..3f071e2 100644 --- a/auth-system/src/main/java/cn/bunny/services/core/event/listener/user/UserinfoUpdateListener.java +++ b/auth-system/src/main/java/cn/bunny/services/core/event/listener/user/UserinfoUpdateListener.java @@ -1,15 +1,15 @@ package cn.bunny.services.core.event.listener.user; -import cn.bunny.services.core.cache.UserLoginVoBuilderCacheService; +import cn.bunny.domain.common.constant.RedisUserConstant; +import cn.bunny.domain.model.system.entity.RolePermission; +import cn.bunny.domain.model.system.entity.UserRole; import cn.bunny.services.core.event.event.ClearAllUserCacheEvent; import cn.bunny.services.core.event.event.UpdateUserinfoByPermissionIdsEvent; import cn.bunny.services.core.event.event.UpdateUserinfoByRoleIdsEvent; import cn.bunny.services.core.event.event.UpdateUserinfoByUserIdsEvent; -import cn.bunny.domain.common.constant.RedisUserConstant; -import cn.bunny.domain.model.system.entity.RolePermission; -import cn.bunny.domain.model.system.entity.UserRole; import cn.bunny.services.mapper.system.RolePermissionMapper; import cn.bunny.services.mapper.system.UserRoleMapper; +import cn.bunny.services.security.service.UserLoginVoBuilderCacheService; import jakarta.annotation.Resource; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; diff --git a/auth-system/src/main/java/cn/bunny/services/core/utils/RouterServiceHelper.java b/auth-system/src/main/java/cn/bunny/services/core/utils/RouterServiceHelper.java index 3f86f53..0b82270 100644 --- a/auth-system/src/main/java/cn/bunny/services/core/utils/RouterServiceHelper.java +++ b/auth-system/src/main/java/cn/bunny/services/core/utils/RouterServiceHelper.java @@ -162,4 +162,93 @@ public class RouterServiceHelper { .sorted(Comparator.comparing(routerVo -> routerVo.getMeta().getRank())) .toList(); } + + /** + * 首先构建一个从节点ID到节点对象的映射,方便快速查找父节点。 + *

+ * 然后遍历所有节点,确保每个父节点包含其直接子节点的所有角色。 + *

+ * 最后进行多次从下往上的传播,确保角色信息从叶子节点一直传播到根节点。这个过程会重复直到没有新的角色需要添加为止。 + */ + public void propagateRolesToParents(List webUserRouterVoList) { + if (webUserRouterVoList == null || webUserRouterVoList.isEmpty()) return; + + // 首先构建一个id到节点的映射,方便查找父节点 + Map idToNodeMap = new HashMap<>(); + buildIdMap(webUserRouterVoList, idToNodeMap); + + // 遍历所有节点,将子节点的角色传播到父节点 + for (WebUserRouterVo node : idToNodeMap.values()) { + if (node.getChildren() != null && !node.getChildren().isEmpty()) { + // 获取当前节点的所有子节点 + List children = node.getChildren(); + + // 收集所有子节点的角色 + RouterMeta parentMeta = node.getMeta(); + if (parentMeta == null) { + parentMeta = new RouterMeta(); + node.setMeta(parentMeta); + } + + Set allRoles = new HashSet<>(); + if (parentMeta.getRoles() != null) { + allRoles.addAll(parentMeta.getRoles()); + } + + for (WebUserRouterVo child : children) { + if (child.getMeta() != null && child.getMeta().getRoles() != null) { + allRoles.addAll(child.getMeta().getRoles()); + } + } + + // 更新父节点的角色 + if (!allRoles.isEmpty()) { + parentMeta.setRoles(new ArrayList<>(allRoles)); + } + } + } + + // 需要从下往上传播角色,所以需要多次遍历直到没有变化 + boolean changed; + do { + changed = false; + for (WebUserRouterVo node : idToNodeMap.values()) { + if (node.getParentId() != null && node.getParentId() != 0) { + WebUserRouterVo parent = idToNodeMap.get(node.getParentId()); + if (parent != null) { + RouterMeta parentMeta = parent.getMeta(); + RouterMeta childMeta = node.getMeta(); + + if (childMeta != null && childMeta.getRoles() != null) { + if (parentMeta == null) { + parentMeta = new RouterMeta(); + parent.setMeta(parentMeta); + } + + Set parentRoles = parentMeta.getRoles() != null + ? new HashSet<>(parentMeta.getRoles()) + : new HashSet<>(); + + int originalSize = parentRoles.size(); + parentRoles.addAll(childMeta.getRoles()); + + if (parentRoles.size() > originalSize) { + parentMeta.setRoles(new ArrayList<>(parentRoles)); + changed = true; + } + } + } + } + } + } while (changed); + } + + private void buildIdMap(List nodes, Map idToNodeMap) { + for (WebUserRouterVo node : nodes) { + idToNodeMap.put(node.getId(), node); + if (node.getChildren() != null && !node.getChildren().isEmpty()) { + buildIdMap(node.getChildren(), idToNodeMap); + } + } + } } diff --git a/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationFailureHandler.java b/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationFailureHandler.java deleted file mode 100644 index c36d811..0000000 --- a/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationFailureHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.bunny.services.security.handelr; - -import cn.bunny.domain.common.model.vo.result.Result; -import com.alibaba.fastjson2.JSON; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; - -import java.io.IOException; - -public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler { - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { - // 错误消息 - String localizedMessage = exception.getLocalizedMessage(); - Result result = Result.error(localizedMessage); - - // 转成JSON - Object json = JSON.toJSON(result); - - // 返回响应 - response.setContentType("application/json;charset=UTF-8"); - response.getWriter().println(json); - } -} diff --git a/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationSuccessHandler.java b/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationSuccessHandler.java deleted file mode 100644 index 79b68c8..0000000 --- a/auth-system/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationSuccessHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.bunny.services.security.handelr; - -import cn.bunny.domain.common.model.vo.result.Result; -import com.alibaba.fastjson2.JSON; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; - -import java.io.IOException; - -/** - * 登录成功 - */ -public class SecurityAuthenticationSuccessHandler implements AuthenticationSuccessHandler { - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - // 获取用户身份信息 - Object principal = authentication.getPrincipal(); - Result result = Result.success(principal); - - // 返回 - response.setContentType("application/json;charset=UTF-8"); - response.getWriter().println(JSON.toJSON(result)); - } -} diff --git a/auth-system/src/main/java/cn/bunny/services/core/cache/UserLoginVoBuilderCacheService.java b/auth-system/src/main/java/cn/bunny/services/security/service/UserLoginVoBuilderCacheService.java similarity index 98% rename from auth-system/src/main/java/cn/bunny/services/core/cache/UserLoginVoBuilderCacheService.java rename to auth-system/src/main/java/cn/bunny/services/security/service/UserLoginVoBuilderCacheService.java index 1d94fb5..47865e0 100644 --- a/auth-system/src/main/java/cn/bunny/services/core/cache/UserLoginVoBuilderCacheService.java +++ b/auth-system/src/main/java/cn/bunny/services/security/service/UserLoginVoBuilderCacheService.java @@ -1,15 +1,15 @@ -package cn.bunny.services.core.cache; +package cn.bunny.services.security.service; -import cn.bunny.services.core.utils.RoleHelper; +import cn.bunny.core.utils.JwtTokenUtil; import cn.bunny.domain.common.constant.LocalDateTimeConstant; import cn.bunny.domain.common.constant.RedisUserConstant; import cn.bunny.domain.common.model.vo.LoginVo; import cn.bunny.domain.model.system.entity.AdminUser; import cn.bunny.domain.model.system.entity.Permission; import cn.bunny.domain.model.system.entity.Role; +import cn.bunny.services.core.utils.RoleHelper; import cn.bunny.services.mapper.system.PermissionMapper; import cn.bunny.services.mapper.system.RoleMapper; -import cn.bunny.core.utils.JwtTokenUtil; import jakarta.annotation.Resource; import lombok.Value; import org.springframework.beans.BeanUtils; diff --git a/auth-system/src/main/java/cn/bunny/services/service/system/impl/RolePermissionServiceImpl.java b/auth-system/src/main/java/cn/bunny/services/service/system/impl/RolePermissionServiceImpl.java index b4dcec3..b30a210 100644 --- a/auth-system/src/main/java/cn/bunny/services/service/system/impl/RolePermissionServiceImpl.java +++ b/auth-system/src/main/java/cn/bunny/services/service/system/impl/RolePermissionServiceImpl.java @@ -1,10 +1,10 @@ package cn.bunny.services.service.system.impl; -import cn.bunny.services.core.event.event.UpdateUserinfoByRoleIdsEvent; import cn.bunny.domain.model.system.dto.AssignPowersToRoleDto; import cn.bunny.domain.model.system.entity.AdminUser; import cn.bunny.domain.model.system.entity.RolePermission; import cn.bunny.domain.model.system.entity.UserRole; +import cn.bunny.services.core.event.event.UpdateUserinfoByRoleIdsEvent; import cn.bunny.services.mapper.system.RolePermissionMapper; import cn.bunny.services.mapper.system.UserMapper; import cn.bunny.services.mapper.system.UserRoleMapper; @@ -65,8 +65,7 @@ public class RolePermissionServiceImpl extends ServiceImpl ids = List.of(roleId); - removeByIds(ids); - // baseMapper.deleteBatchRoleIds(ids); + baseMapper.deleteBatchRoleIds(ids); // 保存分配数据 List rolePermissionList = powerIds.stream().map(powerId -> { diff --git a/auth-system/src/main/java/cn/bunny/services/service/system/impl/RouterServiceImpl.java b/auth-system/src/main/java/cn/bunny/services/service/system/impl/RouterServiceImpl.java index 7366161..abfcdaa 100644 --- a/auth-system/src/main/java/cn/bunny/services/service/system/impl/RouterServiceImpl.java +++ b/auth-system/src/main/java/cn/bunny/services/service/system/impl/RouterServiceImpl.java @@ -1,6 +1,6 @@ package cn.bunny.services.service.system.impl; -import cn.bunny.services.core.utils.RouterServiceHelper; +import cn.bunny.core.exception.AuthCustomerException; import cn.bunny.domain.common.enums.ResultCodeEnum; import cn.bunny.domain.model.system.dto.RouterDto; import cn.bunny.domain.model.system.entity.router.Router; @@ -11,7 +11,7 @@ import cn.bunny.domain.model.system.views.ViewRouterRole; import cn.bunny.domain.model.system.vo.router.RouterManageVo; import cn.bunny.domain.model.system.vo.router.RouterVo; import cn.bunny.domain.model.system.vo.router.WebUserRouterVo; -import cn.bunny.core.exception.AuthCustomerException; +import cn.bunny.services.core.utils.RouterServiceHelper; import cn.bunny.services.mapper.system.RolePermissionMapper; import cn.bunny.services.mapper.system.RouterMapper; import cn.bunny.services.mapper.system.RouterRoleMapper; @@ -78,6 +78,7 @@ public class RouterServiceImpl extends ServiceImpl impleme // 整理web用户所能看到的路由列表,并检查当前用户是否是admin List webUserRouterVoList = routerServiceHelper.getWebUserRouterVos(routerList, routerRoleList, rolePermissionList); + routerServiceHelper.propagateRolesToParents(webUserRouterVoList); // 添加 admin 管理路由权限 webUserRouterVoList.forEach(routerVo -> { diff --git a/auth-system/src/main/java/cn/bunny/services/service/system/impl/UserLoginServiceImpl.java b/auth-system/src/main/java/cn/bunny/services/service/system/impl/UserLoginServiceImpl.java index 4ed387a..0055ad0 100644 --- a/auth-system/src/main/java/cn/bunny/services/service/system/impl/UserLoginServiceImpl.java +++ b/auth-system/src/main/java/cn/bunny/services/service/system/impl/UserLoginServiceImpl.java @@ -1,14 +1,9 @@ package cn.bunny.services.service.system.impl; import cn.bunny.core.context.BaseContext; -import cn.bunny.services.core.cache.EmailCacheService; -import cn.bunny.services.core.cache.UserLoginVoBuilderCacheService; -import cn.bunny.services.core.event.event.ClearAllUserCacheEvent; -import cn.bunny.services.core.strategy.login.DefaultLoginStrategy; -import cn.bunny.services.core.strategy.login.EmailLoginStrategy; -import cn.bunny.services.core.strategy.login.LoginContext; -import cn.bunny.services.core.strategy.login.LoginStrategy; -import cn.bunny.services.core.template.email.ConcreteSenderEmailTemplate; +import cn.bunny.core.exception.AuthCustomerException; +import cn.bunny.core.utils.IpUtil; +import cn.bunny.core.utils.JwtTokenUtil; import cn.bunny.domain.common.constant.RedisUserConstant; import cn.bunny.domain.common.constant.UserConstant; import cn.bunny.domain.common.enums.EmailTemplateEnums; @@ -22,13 +17,18 @@ import cn.bunny.domain.model.system.dto.user.LoginDto; import cn.bunny.domain.model.system.dto.user.RefreshTokenDto; import cn.bunny.domain.model.system.entity.AdminUser; import cn.bunny.domain.model.system.vo.user.RefreshTokenVo; -import cn.bunny.core.exception.AuthCustomerException; +import cn.bunny.services.core.cache.EmailCacheService; +import cn.bunny.services.core.event.event.ClearAllUserCacheEvent; +import cn.bunny.services.core.strategy.login.DefaultLoginStrategy; +import cn.bunny.services.core.strategy.login.EmailLoginStrategy; +import cn.bunny.services.core.strategy.login.LoginContext; +import cn.bunny.services.core.strategy.login.LoginStrategy; +import cn.bunny.services.core.template.email.ConcreteSenderEmailTemplate; import cn.bunny.services.mapper.configuration.EmailTemplateMapper; import cn.bunny.services.mapper.log.UserLoginLogMapper; import cn.bunny.services.mapper.system.UserMapper; +import cn.bunny.services.security.service.UserLoginVoBuilderCacheService; import cn.bunny.services.service.system.UserLoginService; -import cn.bunny.core.utils.IpUtil; -import cn.bunny.core.utils.JwtTokenUtil; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.CircleCaptcha; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; diff --git a/auth-system/src/test/java/impl/BuildPermissionApiTest.java b/auth-system/src/test/java/impl/BuildPermissionApiTest.java deleted file mode 100644 index 39261f6..0000000 --- a/auth-system/src/test/java/impl/BuildPermissionApiTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package impl; - -import cn.bunny.services.AuthServiceApplication; -import cn.bunny.services.aop.scanner.ControllerApiPermissionScanner; -import cn.bunny.domain.common.model.dto.scanner.ScannerControllerInfoVo; -import cn.bunny.domain.model.system.entity.Permission; -import cn.bunny.services.service.system.PermissionService; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import java.util.List; -import java.util.Objects; - -@SpringBootTest(classes = AuthServiceApplication.class, properties = "spring.profiles.active=dev") -public class BuildPermissionApiTest { - - @Autowired - private PermissionService permissionService; - - @Test - void test() { - List list = ControllerApiPermissionScanner.scanControllerInfo(); - - // 添加【Springboot端点】在服务监控中会用到 - ScannerControllerInfoVo actuatorParent = ScannerControllerInfoVo.builder().powerCode("admin:actuator").summary("actuator端点访问").build(); - ScannerControllerInfoVo actuatorChild = ScannerControllerInfoVo.builder().path("/api/actuator/**") - .summary("Springboot端点全部可以访问") - .description("系统监控使用") - .powerCode("actuator:all") - .build(); - actuatorParent.setChildren(List.of(actuatorChild)); - list.add(actuatorParent); - - list.forEach(parent -> { - String summary = parent.getSummary(); - String path = parent.getPath(); - String httpMethod = parent.getHttpMethod(); - String powerCodes = parent.getPowerCode(); - - // 设置 powerCode - String powerCode = Objects.isNull(powerCodes) ? "" : powerCodes; - - Permission permission = new Permission(); - permission.setParentId(0L); - permission.setPowerName(summary); - permission.setPowerCode(powerCode); - permission.setRequestMethod(httpMethod); - permission.setRequestUrl(path); - permissionService.save(permission); - - // 保存后 permission 的 Id 作为子级的父级Id - Long permissionId = permission.getId(); - - // 子级列表 - List permissionList = parent.getChildren().stream() - .map(children -> { - String childrenSummary = children.getSummary(); - String childrenPath = children.getPath(); - String childrenHttpMethod = children.getHttpMethod(); - String childrenPowerCodes = children.getPowerCode(); - - // 设置 powerCode - String childrenPowerCode = Objects.isNull(childrenPowerCodes) ? "" : childrenPowerCodes; - - Permission childPermission = new Permission(); - childPermission.setParentId(permissionId); - childPermission.setPowerName(childrenSummary); - childPermission.setPowerCode(childrenPowerCode); - childPermission.setRequestMethod(childrenHttpMethod); - childPermission.setRequestUrl(childrenPath); - - return childPermission; - }).toList(); - - permissionService.saveBatch(permissionList); - }); - } -} diff --git a/auth-system/src/test/java/impl/ControllerScannerTest.java b/auth-system/src/test/java/impl/ControllerScannerTest.java deleted file mode 100644 index ff21684..0000000 --- a/auth-system/src/test/java/impl/ControllerScannerTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package impl; - -import cn.bunny.services.aop.scanner.ControllerApiPermissionScanner; -import cn.bunny.domain.common.model.dto.scanner.ScannerControllerInfoVo; -import com.alibaba.fastjson2.JSON; -import org.junit.jupiter.api.Test; - -import java.util.List; - -public class ControllerScannerTest { - - @Test - void test() { - List list = ControllerApiPermissionScanner.scanControllerInfo(); - - // 添加【Springboot端点】在服务监控中会用到 - ScannerControllerInfoVo actuatorParent = ScannerControllerInfoVo.builder().powerCode("admin:actuator").summary("actuator端点访问").build(); - ScannerControllerInfoVo actuatorChild = ScannerControllerInfoVo.builder().path("/api/actuator/**") - .summary("Springboot端点全部可以访问") - .description("系统监控使用") - .powerCode("actuator:all") - .build(); - actuatorParent.setChildren(List.of(actuatorChild)); - list.add(actuatorParent); - - String jsonString = JSON.toJSONString(list); - System.out.println(jsonString); - } -}