fix: 修复租户登陆时的权限问题,租户套餐更新时租户菜单权限同步更新

This commit is contained in:
小熊
2025-07-20 12:54:37 +08:00
parent ca1c64f57f
commit 382c87f8bd
11 changed files with 56 additions and 75 deletions

View File

@@ -70,20 +70,15 @@ public class RegexConstants {
* 域名、IPV4、IPV6 * 域名、IPV4、IPV6
* </p> * </p>
*/ */
public static final String HTTP_HOST = public static final String HTTP_HOST = "^(" +
"^(" +
// ① 域名 // ① 域名
"(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}" + "(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}" + "|" +
"|" +
// ② IPv4 // ② IPv4
"(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)" + "(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)" + "|" +
"|" +
// ③ IPv68 组 1-4 位十六进制,用 : 分隔,支持压缩 0 // ③ IPv68 组 1-4 位十六进制,用 : 分隔,支持压缩 0
"(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}" + "(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}" + "|" +
"|" +
// ④ IPv6 压缩形式(:: // ④ IPv6 压缩形式(::
"(?:[0-9A-Fa-f]{1,4}:){0,6}::(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}" + "(?:[0-9A-Fa-f]{1,4}:){0,6}::(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}" + ")$";
")$";
private RegexConstants() { private RegexConstants() {
} }

View File

@@ -64,9 +64,9 @@ public class CommonController {
@SaIgnore @SaIgnore
@TenantIgnore @TenantIgnore
@GetMapping("/id/domain") @GetMapping("/id/domain")
public Long getTenantIdByUrl(@RequestParam("domain") String domain){ public Long getTenantIdByUrl(@RequestParam("domain") String domain) {
TenantDO tenantDO = tenantService.getByDomain(domain); TenantDO tenantDO = tenantService.getByDomain(domain);
if (tenantDO != null){ if (tenantDO != null) {
return tenantDO.getId(); return tenantDO.getId();
} }
return null; return null;

View File

@@ -36,7 +36,6 @@ import top.continew.admin.system.mapper.user.UserPasswordHistoryMapper;
import top.continew.admin.system.mapper.user.UserSocialMapper; import top.continew.admin.system.mapper.user.UserSocialMapper;
import top.continew.admin.system.model.entity.DeptDO; import top.continew.admin.system.model.entity.DeptDO;
import top.continew.admin.system.model.entity.FileDO; import top.continew.admin.system.model.entity.FileDO;
import top.continew.admin.system.model.entity.MenuDO;
import top.continew.admin.system.model.entity.RoleDO; import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.entity.user.UserDO; import top.continew.admin.system.model.entity.user.UserDO;
import top.continew.admin.system.service.FileService; import top.continew.admin.system.service.FileService;
@@ -96,8 +95,6 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
Long deptId = this.initDeptData(tenant); Long deptId = this.initDeptData(tenant);
// 初始化菜单 // 初始化菜单
List<Long> menuIds = packageMenuService.listMenuIdsByPackageId(tenant.getPackageId()); List<Long> menuIds = packageMenuService.listMenuIdsByPackageId(tenant.getPackageId());
List<MenuDO> menuList = menuMapper.lambdaQuery().in(MenuDO::getId, menuIds).list();
this.initMenuData(menuList, 0L, 0L);
// 初始化角色 // 初始化角色
Long roleId = this.initRoleData(tenant); Long roleId = this.initRoleData(tenant);
// 角色绑定菜单 // 角色绑定菜单
@@ -165,24 +162,6 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
return dept.getId(); return dept.getId();
} }
/**
* 递归初始化菜单数据
*
* @param menuList 菜单列表
* @param oldParentId 旧父级 ID
* @param newParentId 新父级 ID
*/
private void initMenuData(List<MenuDO> menuList, Long oldParentId, Long newParentId) {
List<MenuDO> children = menuList.stream().filter(menuDO -> menuDO.getParentId().equals(oldParentId)).toList();
for (MenuDO menu : children) {
Long oldId = menu.getId();
menu.setId(null);
menu.setParentId(newParentId);
menuMapper.insert(menu);
initMenuData(menuList, oldId, menu.getId());
}
}
/** /**
* 初始化角色数据 * 初始化角色数据
* *

View File

@@ -18,13 +18,18 @@ package top.continew.admin.tenant.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.alicp.jetcache.anno.Cached; import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.ahoo.cosid.provider.IdGeneratorProvider; import me.ahoo.cosid.provider.IdGeneratorProvider;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import top.continew.admin.common.base.service.BaseServiceImpl; import top.continew.admin.common.base.service.BaseServiceImpl;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.model.entity.MenuDO; import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.service.MenuService; import top.continew.admin.system.model.entity.RoleMenuDO;
import top.continew.admin.system.service.RoleMenuService;
import top.continew.admin.system.service.RoleService;
import top.continew.admin.tenant.config.TenantExtensionProperties; import top.continew.admin.tenant.config.TenantExtensionProperties;
import top.continew.admin.tenant.constant.TenantCacheConstants; import top.continew.admin.tenant.constant.TenantCacheConstants;
import top.continew.admin.tenant.constant.TenantConstants; import top.continew.admin.tenant.constant.TenantConstants;
@@ -35,16 +40,15 @@ import top.continew.admin.tenant.model.query.TenantQuery;
import top.continew.admin.tenant.model.req.TenantReq; import top.continew.admin.tenant.model.req.TenantReq;
import top.continew.admin.tenant.model.resp.TenantDetailResp; import top.continew.admin.tenant.model.resp.TenantDetailResp;
import top.continew.admin.tenant.model.resp.TenantResp; import top.continew.admin.tenant.model.resp.TenantResp;
import top.continew.admin.tenant.service.PackageMenuService;
import top.continew.admin.tenant.service.PackageService; import top.continew.admin.tenant.service.PackageService;
import top.continew.admin.tenant.service.TenantService; import top.continew.admin.tenant.service.TenantService;
import top.continew.starter.cache.redisson.util.RedisUtils; import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.validation.CheckUtils; import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.tenant.util.TenantUtils; import top.continew.starter.extension.tenant.util.TenantUtils;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@@ -62,8 +66,8 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
private final PackageService packageService; private final PackageService packageService;
private final IdGeneratorProvider idGeneratorProvider; private final IdGeneratorProvider idGeneratorProvider;
private final TenantDataHandler tenantDataHandler; private final TenantDataHandler tenantDataHandler;
private final PackageMenuService packageMenuService; private final RoleMenuService roleMenuService;
private final MenuService menuService; private final RoleService roleService;
@Override @Override
public Long create(TenantReq req) { public Long create(TenantReq req) {
@@ -147,26 +151,20 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
if (CollUtil.isEmpty(tenantIdList)) { if (CollUtil.isEmpty(tenantIdList)) {
return; return;
} }
List<Long> oldMenuIds = packageMenuService.listMenuIdsByPackageId(packageId); //删除旧菜单
// 如果有删除的菜单则绑定了套餐的租户对应的菜单也会删除 tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> roleMenuService.remove(Wrappers
List<Long> deleteMenuIds = new ArrayList<>(oldMenuIds); .lambdaQuery(RoleMenuDO.class)
deleteMenuIds.removeAll(newMenuIds); .notIn(RoleMenuDO::getMenuId, newMenuIds))));
if (CollUtil.isNotEmpty(deleteMenuIds)) { //新增菜单
List<MenuDO> deleteMenus = menuService.listByIds(deleteMenuIds); tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> {
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> menuService RoleDO roleDO = roleService.getByCode(SysConstants.TENANT_ADMIN_ROLE_CODE);
.deleteTenantMenus(deleteMenus))); List<Long> oldMenuIds = roleMenuService.list(Wrappers.lambdaQuery(RoleMenuDO.class)
} .eq(RoleMenuDO::getRoleId, roleDO.getId())).stream().map(RoleMenuDO::getMenuId).toList();
// 如果有新增的菜单则绑定了套餐的租户对应的菜单也会新增 newMenuIds.removeAll(oldMenuIds);
List<Long> addMenuIds = new ArrayList<>(newMenuIds); roleMenuService.add(newMenuIds, roleDO.getId());
addMenuIds.removeAll(oldMenuIds); }));
if (CollUtil.isNotEmpty(addMenuIds)) { //清理角色菜单缓存
List<MenuDO> addMenus = menuService.listByIds(addMenuIds); RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
for (MenuDO addMenu : addMenus) {
MenuDO parentMenu = addMenu.getParentId() != 0 ? menuService.getById(addMenu.getParentId()) : null;
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> menuService
.addTenantMenu(addMenu, parentMenu)));
}
}
} }
@Override @Override

View File

@@ -223,6 +223,7 @@ continew-starter.tenant:
- sys_sms_log # 短信日志表 - sys_sms_log # 短信日志表
- sys_client # 客户端表 - sys_client # 客户端表
- sys_app # 应用表 - sys_app # 应用表
- sys_menu
# 忽略菜单 ID租户不能使用的菜单 # 忽略菜单 ID租户不能使用的菜单
ignore-menus: ignore-menus:
- 1130 # 字典管理 - 1130 # 字典管理

View File

@@ -48,9 +48,6 @@ CREATE TABLE IF NOT EXISTS `tenant_package_menu` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户套餐和菜单关联表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户套餐和菜单关联表';
-- 为已有表增加租户字段 -- 为已有表增加租户字段
ALTER TABLE `sys_menu`
ADD COLUMN `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
ADD INDEX `idx_tenant_id` (`tenant_id`);
ALTER TABLE `sys_dept` ALTER TABLE `sys_dept`
ADD COLUMN `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID', ADD COLUMN `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
ADD INDEX `idx_tenant_id` (`tenant_id`); ADD INDEX `idx_tenant_id` (`tenant_id`);
@@ -98,9 +95,6 @@ ALTER TABLE `sys_app`
ADD INDEX `idx_tenant_id` (`tenant_id`); ADD INDEX `idx_tenant_id` (`tenant_id`);
-- 调整唯一索引 -- 调整唯一索引
ALTER TABLE `sys_menu`
DROP INDEX `uk_title_parent_id`,
ADD UNIQUE INDEX `uk_title_parent_id` (`title`, `parent_id`, `tenant_id`);
ALTER TABLE `sys_dept` ALTER TABLE `sys_dept`
DROP INDEX `uk_name_parent_id`, DROP INDEX `uk_name_parent_id`,
ADD UNIQUE INDEX `uk_name_parent_id` (`name`, `parent_id`, `tenant_id`); ADD UNIQUE INDEX `uk_name_parent_id` (`name`, `parent_id`, `tenant_id`);

View File

@@ -72,7 +72,10 @@ public class AccountLoginHandler extends AbstractLoginHandler<AccountLoginReq> {
super.checkUserStatus(user); super.checkUserStatus(user);
// 执行认证 // 执行认证
String token = this.authenticate(user, client); String token = this.authenticate(user, client);
return LoginResp.builder().token(token).tenantId(TenantContextHolder.isTenantEnabled()? TenantContextHolder.getTenantId():null).build(); return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
} }
@Override @Override

View File

@@ -48,7 +48,11 @@ public class EmailLoginHandler extends AbstractLoginHandler<EmailLoginReq> {
super.checkUserStatus(user); super.checkUserStatus(user);
// 执行认证 // 执行认证
String token = super.authenticate(user, client); String token = super.authenticate(user, client);
return LoginResp.builder().token(token).tenantId(TenantContextHolder.isTenantEnabled()? TenantContextHolder.getTenantId():null).build(); } return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
}
@Override @Override
public void preLogin(EmailLoginReq req, ClientResp client, HttpServletRequest request) { public void preLogin(EmailLoginReq req, ClientResp client, HttpServletRequest request) {

View File

@@ -48,7 +48,11 @@ public class PhoneLoginHandler extends AbstractLoginHandler<PhoneLoginReq> {
super.checkUserStatus(user); super.checkUserStatus(user);
// 执行认证 // 执行认证
String token = super.authenticate(user, client); String token = super.authenticate(user, client);
return LoginResp.builder().token(token).tenantId(TenantContextHolder.isTenantEnabled()? TenantContextHolder.getTenantId():null).build(); } return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
}
@Override @Override
public void preLogin(PhoneLoginReq req, ClientResp client, HttpServletRequest request) { public void preLogin(PhoneLoginReq req, ClientResp client, HttpServletRequest request) {

View File

@@ -128,7 +128,10 @@ public class SocialLoginHandler extends AbstractLoginHandler<SocialLoginReq> {
userSocialService.saveOrUpdate(userSocial); userSocialService.saveOrUpdate(userSocial);
// 执行认证 // 执行认证
String token = super.authenticate(user, client); String token = super.authenticate(user, client);
return LoginResp.builder().token(token).tenantId(TenantContextHolder.isTenantEnabled()? TenantContextHolder.getTenantId():null).build(); return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
} }
@Override @Override

View File

@@ -130,10 +130,10 @@ public class CommonController {
@TenantIgnore @TenantIgnore
@SaIgnore @SaIgnore
@Operation(summary = "查询租户开启状态",description = "查询租户开启状态") @Operation(summary = "查询租户开启状态", description = "查询租户开启状态")
@GetMapping("/dict/option/tenant") @GetMapping("/dict/option/tenant")
@Cached(key = "'TENANT'", name = CacheConstants.OPTION_KEY_PREFIX) @Cached(key = "'TENANT'", name = CacheConstants.OPTION_KEY_PREFIX)
public Boolean tenantEnabled(){ public Boolean tenantEnabled() {
return TenantContextHolder.isTenantEnabled(); return TenantContextHolder.isTenantEnabled();
} }
} }