✨ 父菜单角色继承子菜单角色
This commit is contained in:
parent
1aeaeaea72
commit
34913542c0
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -162,4 +162,93 @@ public class RouterServiceHelper {
|
|||
.sorted(Comparator.comparing(routerVo -> routerVo.getMeta().getRank()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 首先构建一个从节点ID到节点对象的映射,方便快速查找父节点。
|
||||
* <p>
|
||||
* 然后遍历所有节点,确保每个父节点包含其直接子节点的所有角色。
|
||||
* <p>
|
||||
* 最后进行多次从下往上的传播,确保角色信息从叶子节点一直传播到根节点。这个过程会重复直到没有新的角色需要添加为止。
|
||||
*/
|
||||
public void propagateRolesToParents(List<WebUserRouterVo> webUserRouterVoList) {
|
||||
if (webUserRouterVoList == null || webUserRouterVoList.isEmpty()) return;
|
||||
|
||||
// 首先构建一个id到节点的映射,方便查找父节点
|
||||
Map<Long, WebUserRouterVo> idToNodeMap = new HashMap<>();
|
||||
buildIdMap(webUserRouterVoList, idToNodeMap);
|
||||
|
||||
// 遍历所有节点,将子节点的角色传播到父节点
|
||||
for (WebUserRouterVo node : idToNodeMap.values()) {
|
||||
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
|
||||
// 获取当前节点的所有子节点
|
||||
List<WebUserRouterVo> children = node.getChildren();
|
||||
|
||||
// 收集所有子节点的角色
|
||||
RouterMeta parentMeta = node.getMeta();
|
||||
if (parentMeta == null) {
|
||||
parentMeta = new RouterMeta();
|
||||
node.setMeta(parentMeta);
|
||||
}
|
||||
|
||||
Set<String> 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<String> 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<WebUserRouterVo> nodes, Map<Long, WebUserRouterVo> idToNodeMap) {
|
||||
for (WebUserRouterVo node : nodes) {
|
||||
idToNodeMap.put(node.getId(), node);
|
||||
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
|
||||
buildIdMap(node.getChildren(), idToNodeMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> result = Result.error(localizedMessage);
|
||||
|
||||
// 转成JSON
|
||||
Object json = JSON.toJSON(result);
|
||||
|
||||
// 返回响应
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().println(json);
|
||||
}
|
||||
}
|
|
@ -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<Object> result = Result.success(principal);
|
||||
|
||||
// 返回
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().println(JSON.toJSON(result));
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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<RolePermissionMapper,
|
|||
|
||||
// 删除这个角色下所有权限
|
||||
List<Long> ids = List.of(roleId);
|
||||
removeByIds(ids);
|
||||
// baseMapper.deleteBatchRoleIds(ids);
|
||||
baseMapper.deleteBatchRoleIds(ids);
|
||||
|
||||
// 保存分配数据
|
||||
List<RolePermission> rolePermissionList = powerIds.stream().map(powerId -> {
|
||||
|
|
|
@ -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<RouterMapper, Router> impleme
|
|||
|
||||
// 整理web用户所能看到的路由列表,并检查当前用户是否是admin
|
||||
List<WebUserRouterVo> webUserRouterVoList = routerServiceHelper.getWebUserRouterVos(routerList, routerRoleList, rolePermissionList);
|
||||
routerServiceHelper.propagateRolesToParents(webUserRouterVoList);
|
||||
|
||||
// 添加 admin 管理路由权限
|
||||
webUserRouterVoList.forEach(routerVo -> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<ScannerControllerInfoVo> 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<Permission> 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<ScannerControllerInfoVo> 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue