From 45482a5dbf3107d6716a07bd6c659dfb8fdda6c7 Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Fri, 29 Nov 2024 17:04:52 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E7=A6=85=E9=81=93#795=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E5=A4=8D=EF=BC=8C=E6=94=AF=E6=8C=81=E9=83=A8?= =?UTF-8?q?=E9=97=A8=E7=A6=81=E7=94=A8=E7=9A=84=E5=AE=9E=E6=97=B6=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=EF=BC=9B2=E3=80=81=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E3=80=81=E9=83=A8=E9=97=A8=E3=80=81=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=92=8C=E7=A6=81=E7=94=A8=E6=97=B6=E5=AF=B9?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=94=A8=E6=88=B7=E7=9A=84token=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ot.autoconfigure.AutoConfiguration.imports | 4 +- .../ChenfengOrganAutoConfiguration.java | 5 +- .../core/security/OrganSecurityWebFilter.java | 5 + .../core/service/OrganFrameworkService.java | 6 + .../service/OrganFrameworkServiceImpl.java | 7 ++ .../imes/module/system/api/dept/DeptApi.java | 6 + .../system/enums/ErrorCodeConstants.java | 1 + .../module/system/api/dept/DeptApiImpl.java | 5 + .../system/service/dept/DeptService.java | 6 + .../system/service/dept/DeptServiceImpl.java | 86 +++++++++++++- .../service/organ/OrganServiceImpl.java | 49 +++++++- .../service/user/AdminUserServiceImpl.java | 108 ++++++++++++------ 12 files changed, 246 insertions(+), 42 deletions(-) diff --git a/cf-framework/cf-spring-boot-starter-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cf-framework/cf-spring-boot-starter-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 1f7eb5791..d92092d1e 100644 --- a/cf-framework/cf-spring-boot-starter-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/cf-framework/cf-spring-boot-starter-biz-data-permission/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,2 @@ -com.cf.imes.framework.datapermission.config.ChenfengDataPermissionAutoConfiguration -com.cf.imes.framework.datapermission.config.ChenfengDeptDataPermissionAutoConfiguration +#com.cf.imes.framework.datapermission.config.ChenfengDataPermissionAutoConfiguration +#com.cf.imes.framework.datapermission.config.ChenfengDeptDataPermissionAutoConfiguration diff --git a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/config/ChenfengOrganAutoConfiguration.java b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/config/ChenfengOrganAutoConfiguration.java index 5e0b362b1..51329d918 100644 --- a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/config/ChenfengOrganAutoConfiguration.java +++ b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/config/ChenfengOrganAutoConfiguration.java @@ -16,6 +16,7 @@ import com.cf.imes.framework.organ.core.service.OrganFrameworkServiceImpl; import com.cf.imes.framework.organ.core.web.OrganContextWebFilter; import com.cf.imes.framework.web.config.WebProperties; import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler; +import com.cf.imes.module.system.api.dept.DeptApi; import com.cf.imes.module.system.api.organ.OrganApi; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; @@ -42,8 +43,8 @@ import java.util.Objects; public class ChenfengOrganAutoConfiguration { @Bean - public OrganFrameworkService tenantFrameworkService(OrganApi organApi) { - return new OrganFrameworkServiceImpl(organApi); + public OrganFrameworkService tenantFrameworkService(OrganApi organApi, DeptApi deptApi) { + return new OrganFrameworkServiceImpl(organApi, deptApi); } // ========== AOP ========== diff --git a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/security/OrganSecurityWebFilter.java b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/security/OrganSecurityWebFilter.java index 00b9d1fd7..4a7fcbcff 100644 --- a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/security/OrganSecurityWebFilter.java +++ b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/security/OrganSecurityWebFilter.java @@ -61,8 +61,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter { throws ServletException, IOException { Long organId = WebFrameworkUtils.getOrganId(request); // 1. 登陆的用户,校验是否有权限访问该组织,避免越权问题。 + Long deptId = null; LoginUser user = SecurityFrameworkUtils.getLoginUser(); if (user != null) { + deptId = user.getDeptId(); // 如果获取不到组织编号,则尝试使用登陆用户的组织编号 if (organId == null) { organId = user.getOrganId(); @@ -89,7 +91,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter { } // 3. 校验组织是合法,例如说被禁用、到期 try { + // 校验组织 organFrameworkService.validOrgan(organId); + // 校验部门 + organFrameworkService.validDept(deptId); } catch (Throwable ex) { CommonResult result = globalExceptionHandler.allExceptionHandler(request, ex); // 组织失效返回401踢出系统 diff --git a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkService.java b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkService.java index 9bb84d026..0715c7837 100644 --- a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkService.java +++ b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkService.java @@ -23,4 +23,10 @@ public interface OrganFrameworkService { */ void validOrgan(Long id); + /** + * 校验部门是否合法 + * + * @param id + */ + void validDept(Long id); } diff --git a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkServiceImpl.java b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkServiceImpl.java index a065cdddc..074dd0536 100644 --- a/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkServiceImpl.java +++ b/cf-framework/cf-spring-boot-starter-biz-organ/src/main/java/com/cf/imes/framework/organ/core/service/OrganFrameworkServiceImpl.java @@ -1,6 +1,7 @@ package com.cf.imes.framework.organ.core.service; import com.cf.imes.framework.common.util.cache.CacheUtils; +import com.cf.imes.module.system.api.dept.DeptApi; import com.cf.imes.module.system.api.organ.OrganApi; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -20,6 +21,8 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService { private final OrganApi organApi; + private final DeptApi deptApi; + /** * 针对 {@link #getOrganIds()} 的缓存 */ @@ -46,4 +49,8 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService { organApi.validOrgan(id).checkError(); } + @Override + public void validDept(Long id) { + deptApi.validateDept(id); + } } diff --git a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/dept/DeptApi.java b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/dept/DeptApi.java index 4b3c7b204..e3fb6e315 100644 --- a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/dept/DeptApi.java +++ b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/api/dept/DeptApi.java @@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; @@ -37,6 +38,11 @@ public interface DeptApi { @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) CommonResult validateDeptList(@RequestParam("ids") Collection ids); + @GetMapping(PREFIX + "/valid/{deptId}") + @Operation(summary = "校验部门是否合法") + @Parameter(name = "id", description = "部门编号", example = "1024", required = true) + CommonResult validateDept(@PathVariable("id") Long deptId); + /** * 获得指定编号的部门 Map * diff --git a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java index 2847dc8ab..29595792c 100644 --- a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java +++ b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java @@ -70,6 +70,7 @@ public class ErrorCodeConstants { public static final ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); public static final ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); public static final ErrorCode DEPT_USER_OPER_NOT_ALLOW = new ErrorCode(1_002_015_008, "不允许操作用户自身部门"); + public static final ErrorCode DEPT_DISABLE = new ErrorCode(1_002_004_006, "部门({})已被禁用"); // ========== 岗位模块 1-002-005-000 ========== public static final ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/dept/DeptApiImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/dept/DeptApiImpl.java index 5b403baca..666b1ea42 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/dept/DeptApiImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/api/dept/DeptApiImpl.java @@ -39,4 +39,9 @@ public class DeptApiImpl implements DeptApi { return success(true); } + @Override + public CommonResult validateDept(Long deptId) { + deptService.validDept(deptId); + return success(true); + } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptService.java index 607d8c136..e26d5577c 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptService.java @@ -99,4 +99,10 @@ public interface DeptService { */ void validateDeptList(Collection ids); + /** + * 校验部门是否存在且有效(包括上级) + * + * @param deptId + */ + void validDept(Long deptId); } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptServiceImpl.java index 456310cda..55cda4932 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/dept/DeptServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; +import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.datapermission.core.annotation.DataPermission; import com.cf.imes.framework.security.core.LoginUser; @@ -11,6 +12,7 @@ import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; +import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import com.cf.imes.module.system.dal.mysql.dept.DeptMapper; import com.cf.imes.module.system.dal.redis.RedisKeyConstants; import com.cf.imes.module.system.enums.ErrorCodeConstants; @@ -20,6 +22,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Lazy; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -28,6 +33,7 @@ import java.util.*; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet; +import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; import static com.cf.imes.module.system.enums.ErrorCodeConstants.DEPT_USER_OPER_NOT_ALLOW; /** @@ -47,6 +53,9 @@ public class DeptServiceImpl implements DeptService { @Lazy // 延迟,避免循环依赖报错 private AdminUserService userService; + @Resource + private StringRedisTemplate stringRedisTemplate; + @Override @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 @@ -69,19 +78,26 @@ public class DeptServiceImpl implements DeptService { @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 public void updateDept(DeptSaveReqVO updateReqVO) { + Long deptId = updateReqVO.getId(); if (updateReqVO.getParentId() == null) { updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT); } // 校验自己存在 - validateDeptExists(updateReqVO.getId()); + validateDeptExists(deptId); // 校验父部门的有效性 - validateParentDept(updateReqVO.getId(), updateReqVO.getParentId()); + validateParentDept(deptId, updateReqVO.getParentId()); // 校验部门名的唯一性 - validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getOrganId()); + validateDeptNameUnique(deptId, updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getOrganId()); + // 校验部门内用户操作 + checkCurrentWhenOperate(deptId); // 更新部门 DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class); deptMapper.updateById(updateObj); + if (CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) { + // 移除部门下用户的token + scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), deptId); + } } @Override @@ -100,6 +116,35 @@ public class DeptServiceImpl implements DeptService { deptMapper.deleteById(id); // 删除关联的用户 userService.deleteDeptUsers(id); + // 移除部门下用户的token + scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id); + } + + /** + * 轮训redis token,移除对应部门下的用户的token + * + * @param keyPattern + * @param deptId + */ + private void scanAndCompareDeptAndDelToken(String keyPattern, Long deptId) { + // 根据keyPattern scan匹配的redis key + List matchKeys = new ArrayList<>(); + Cursor cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(keyPattern).count(200).build()); + while (cursor.hasNext()) { + matchKeys.add(cursor.next()); + } + cursor.close(); + if (CollUtil.isNotEmpty(matchKeys)) { + for (String key : matchKeys) { + // 获取key下的用户信息 + OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class); + if (ObjectUtil.equal(deptId, oAuth2AccessTokenDO.getDeptId())) { + // 用户id匹配上了删除redis中的token缓存 + stringRedisTemplate.delete(key); + return; + } + } + } } /** @@ -239,4 +284,39 @@ public class DeptServiceImpl implements DeptService { }); } + @Override + public void validDept(Long deptId) { + DeptDO deptDO = deptMapper.selectById(deptId); + if (deptDO == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_NOT_FOUND); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(deptDO.getStatus())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_DISABLE, deptDO.getName()); + } + // 递归校验上级部门 + validParentDept(deptDO); + } + + /** + * 递归校验上级部门 + * + * @param deptDO + */ + private void validParentDept(DeptDO deptDO) { + if (ObjectUtil.isNotNull(deptDO) && ObjectUtil.isNotNull(deptDO.getParentId())) { + Long parentId = deptDO.getParentId(); + DeptDO parentDept = deptMapper.selectById(parentId); + if (ObjectUtil.equal(parentId, 0L)) { + // 上级部门id是0代表是机构下最顶级的部门 + return; + } + if (parentDept == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_PARENT_NOT_EXITS); + } else if (!CommonStatusEnum.ENABLE.getStatus().equals(parentDept.getStatus())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_DISABLE, parentDept.getName()); + } else { + validParentDept(parentDept); + } + } + } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/organ/OrganServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/organ/OrganServiceImpl.java index c5d3c3068..b3a67837a 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/organ/OrganServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/organ/OrganServiceImpl.java @@ -9,6 +9,7 @@ import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.collection.CollectionUtils; import com.cf.imes.framework.common.util.date.DateUtils; +import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.common.util.pinyin.PinYinUtils; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -27,6 +28,7 @@ import com.cf.imes.module.system.controller.admin.organ.vo.organ.OrganSaveReqVO; import com.cf.imes.module.system.controller.admin.user.vo.user.UserSaveReqVO; import com.cf.imes.module.system.convert.organ.OrganConvert; import com.cf.imes.module.system.dal.dataobject.machine.MachineLimitDO; +import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import com.cf.imes.module.system.dal.dataobject.permission.MenuDO; import com.cf.imes.module.system.dal.dataobject.permission.RoleDO; import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO; @@ -46,6 +48,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -55,6 +60,7 @@ import java.time.LocalDateTime; import java.util.*; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; import static java.util.Collections.singleton; @@ -93,6 +99,9 @@ public class OrganServiceImpl implements OrganService { @Resource private MachineLimitMapper machineLimitMapper; + @Resource + private StringRedisTemplate stringRedisTemplate; + @Override public List getOrganIdList() { @@ -205,12 +214,13 @@ public class OrganServiceImpl implements OrganService { @Override @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 public void updateOrgan(OrganSaveReqVO updateReqVO) { + Long organId = updateReqVO.getId(); // 校验存在 - OrganizationDO tenant = validateUpdateTenant(updateReqVO.getId()); + OrganizationDO tenant = validateUpdateTenant(organId); // 校验组织名称是否重复 - validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId()); + validTenantNameDuplicate(updateReqVO.getName(), organId); // 校验组织域名是否重复 - validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId()); + validTenantWebsiteDuplicate(updateReqVO.getWebsite(), organId); // 校验套餐被禁用 TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); if(!Objects.equals(tenant.getName(), updateReqVO.getName())) { @@ -228,6 +238,10 @@ public class OrganServiceImpl implements OrganService { if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) { permissionService.flushCacheWhenTenantPackageChange(List.of(tenant.getId())); } + if (CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) { + // 移除机构下用户的token + scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), organId); + } } private void validTenantNameDuplicate(String name, Long id) { @@ -297,6 +311,35 @@ public class OrganServiceImpl implements OrganService { organMapper.deleteById(id); // 删除关联的用户 userService.deleteOrgUsers(id); + // 移除机构下用户的token + scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id); + } + + /** + * 轮训redis token,移除对应机构下的用户的token + * + * @param keyPattern + * @param organId + */ + private void scanAndCompareDeptAndDelToken(String keyPattern, Long organId) { + // 根据keyPattern scan匹配的redis key + List matchKeys = new ArrayList<>(); + Cursor cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(keyPattern).count(200).build()); + while (cursor.hasNext()) { + matchKeys.add(cursor.next()); + } + cursor.close(); + if (CollUtil.isNotEmpty(matchKeys)) { + for (String key : matchKeys) { + // 获取key下的用户信息 + OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class); + if (ObjectUtil.equal(organId, oAuth2AccessTokenDO.getOrganId())) { + // 用户id匹配上了删除redis中的token缓存 + stringRedisTemplate.delete(key); + return; + } + } + } } /** diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/user/AdminUserServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/user/AdminUserServiceImpl.java index 03e21d157..bb77fb7bd 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/user/AdminUserServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/user/AdminUserServiceImpl.java @@ -12,11 +12,11 @@ import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.collection.CollectionUtils; +import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.framework.common.util.monitor.TracerUtils; import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.common.util.pinyin.PinYinUtils; import com.cf.imes.framework.common.util.servlet.ServletUtils; -import com.cf.imes.framework.datapermission.core.util.DataPermissionUtils; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX; import com.cf.imes.framework.organ.core.aop.OrganIgnore; @@ -37,6 +37,7 @@ import com.cf.imes.module.system.controller.admin.user.vo.user.UserResetPassword import com.cf.imes.module.system.controller.admin.user.vo.user.UserSaveReqVO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.UserPostDO; +import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import com.cf.imes.module.system.dal.dataobject.permission.UserRoleDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; import com.cf.imes.module.system.dal.mysql.dept.UserPostMapper; @@ -56,6 +57,9 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -71,6 +75,7 @@ import java.util.regex.Pattern; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertList; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet; +import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; import static com.cf.imes.module.system.enums.ErrorCodeConstants.USER_ME_ERROR; import static com.cf.imes.module.system.enums.ErrorCodeConstants.USER_OPERATE_SELF_STATUS_ERROR; @@ -113,6 +118,8 @@ public class AdminUserServiceImpl implements AdminUserService { @Resource private LoginLogService loginLogService; + @Resource + private StringRedisTemplate stringRedisTemplate; // 定义密码校验的正则表达式 private static final String LENGTH_PATTERN = ".{8,}"; @@ -125,7 +132,7 @@ public class AdminUserServiceImpl implements AdminUserService { @Override @Transactional(rollbackFor = Exception.class) public Long createUser(UserSaveReqVO createReqVO) { - Long organId = createReqVO.getOrganId() == null ? OrganContextHolder.getOrganId(): createReqVO.getOrganId(); + Long organId = createReqVO.getOrganId() == null ? OrganContextHolder.getOrganId() : createReqVO.getOrganId(); // 校验账户配合 organService.handleOrganInfo(organ -> { long count = userMapper.selectCount(new LambdaQueryWrapperX().eq(AdminUserDO::getOrganId, organId)); @@ -160,17 +167,18 @@ public class AdminUserServiceImpl implements AdminUserService { @Override @Transactional(rollbackFor = Exception.class) public void updateUser(UserSaveReqVO updateReqVO) { - Long organId = updateReqVO.getOrganId() == null ? OrganContextHolder.getOrganId(): updateReqVO.getOrganId(); + Long organId = updateReqVO.getOrganId() == null ? OrganContextHolder.getOrganId() : updateReqVO.getOrganId(); String mobile = updateReqVO.getMobile(); + Long userId = updateReqVO.getId(); // 不允许关闭自身状态 - checkCurrentWhenOperateStatus(updateReqVO.getId(), updateReqVO.getStatus()); + checkCurrentWhenOperateStatus(userId, updateReqVO.getStatus()); // 校验正确性 - validateUserForCreateOrUpdate(updateReqVO.getId(), mobile, + AdminUserDO currUserDO = validateUserForCreateOrUpdate(userId, mobile, updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds(), null, organId); // 更新用户 AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); - if(!Objects.isNull(updateReqVO.getNickname())) { + if (!Objects.isNull(updateReqVO.getNickname())) { String pinyinFull = PinYinUtils.convertToPinyin(updateReqVO.getNickname()); if (StringUtils.isNotBlank(pinyinFull)) { @@ -192,6 +200,37 @@ public class AdminUserServiceImpl implements AdminUserService { userMapper.updateById(updateObj); // 更新岗位 updateUserPost(updateReqVO, updateObj); + // 如果改变了部门或修改状态为禁用移除用户token + if (ObjectUtil.notEqual(currUserDO.getDeptId(), updateReqVO.getDeptId()) || CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) { + scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), userId); + } + } + + /** + * 轮训redis token,移除对应用户的token + * + * @param keyPattern + * @param userId + */ + private void scanAndCompareUserAndDelToken(String keyPattern, Long userId) { + // 根据keyPattern scan匹配的redis key + List matchKeys = new ArrayList<>(); + Cursor cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(keyPattern).count(200).build()); + while (cursor.hasNext()) { + matchKeys.add(cursor.next()); + } + cursor.close(); + if (CollUtil.isNotEmpty(matchKeys)) { + for (String key : matchKeys) { + // 获取key下的用户信息 + OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class); + if (ObjectUtil.equal(userId, oAuth2AccessTokenDO.getUserId())) { + // 用户id匹配上了删除redis中的token缓存 + stringRedisTemplate.delete(key); + return; + } + } + } } private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) { @@ -313,6 +352,10 @@ public class AdminUserServiceImpl implements AdminUserService { updateObj.setId(id); updateObj.setStatus(status); userMapper.updateById(updateObj); + if (CommonStatusEnum.DISABLE.getStatus().equals(status)) { + // 移除用户token + scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id); + } } @Override @@ -329,6 +372,8 @@ public class AdminUserServiceImpl implements AdminUserService { permissionService.processUserDeleted(id); // 删除用户岗位 userPostMapper.deleteByUserId(id); + // 移除用户token + scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id); } } @@ -479,27 +524,25 @@ public class AdminUserServiceImpl implements AdminUserService { return deptIds; } - private void validateUserForCreateOrUpdate(Long id, String username, String email, - Long deptId, Set postIds,String password ,Long organId) { - // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 - DataPermissionUtils.executeIgnore(() -> { - // 校验用户的密码是否符合规则 - if(password != null) { - validatePassword(password); - } - // 校验用户存在 - validateUserExists(id); - // 校验用户名唯一 - validateUsernameUnique(id, username, organId); - // 校验部门处于开启状态 - deptService.validateDeptList(CollectionUtils.singleton(deptId)); - // 校验岗位处于开启状态 - postService.validatePostList(postIds); - }); + private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String email, + Long deptId, Set postIds, String password, Long organId) { + // 校验用户的密码是否符合规则 + if (password != null) { + validatePassword(password); + } + // 校验用户存在 + AdminUserDO adminUserDO = validateUserExists(id); + // 校验用户名唯一 + validateUsernameUnique(id, username, organId); + // 校验部门处于开启状态 + deptService.validDept(deptId); + // 校验岗位处于开启状态 + postService.validatePostList(postIds); + return adminUserDO; } @VisibleForTesting - void validatePassword(String password){ + void validatePassword(String password) { // 检查密码长度 if (!Pattern.matches(LENGTH_PATTERN, password)) { @@ -526,17 +569,18 @@ public class AdminUserServiceImpl implements AdminUserService { throw ServiceExceptionUtil.exception(ErrorCodeConstants.THE_PASSWORD_LENGTH_MUST_BE_AT_LEAST_8_CHARACTERS); } - } + } @VisibleForTesting - void validateUserExists(Long id) { + AdminUserDO validateUserExists(Long id) { if (id == null) { - return; + return null; } AdminUserDO user = userMapper.selectById(id); if (user == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS); } + return user; } @VisibleForTesting @@ -631,7 +675,7 @@ public class AdminUserServiceImpl implements AdminUserService { // 校验,判断是否有不符合的原因 try { validateUserForCreateOrUpdate(null, null, importUser.getEmail(), - importUser.getDeptId(), null,null, organId); + importUser.getDeptId(), null, null, organId); } catch (ServiceException ex) { respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage()); return; @@ -673,7 +717,7 @@ public class AdminUserServiceImpl implements AdminUserService { public List getUserListByTerms(Integer status, String name) { String namePyFirstChar = ""; String namePyAll = ""; - if(StringUtils.isNotBlank(name)) { + if (StringUtils.isNotBlank(name)) { namePyFirstChar = PinYinUtils.convertToPinyin(name); namePyAll = PinYinUtils.convertToPinyin(name).replace(" ", ""); } @@ -683,11 +727,11 @@ public class AdminUserServiceImpl implements AdminUserService { @Override @OrganIgnore public List getOrganAdminByOrganIds(Collection organIds) { - if(CollUtil.isEmpty(organIds)) { + if (CollUtil.isEmpty(organIds)) { return new ArrayList<>(); } MPJLambdaWrapperX wrapperX = new MPJLambdaWrapperX<>(); - wrapperX.leftJoin(UserRoleDO.class, UserRoleDO::getUserId, AdminUserDO::getId) + wrapperX.leftJoin(UserRoleDO.class, UserRoleDO::getUserId, AdminUserDO::getId) .eq(UserRoleDO::getRoleId, InternalRoleConstants.ORGAN_ADMIN_ROLE_ID) ; List organAdminUserRespDTOS = userMapper.selectJoinList(OrganAdminUserRespDTO.class, wrapperX); @@ -777,7 +821,7 @@ public class AdminUserServiceImpl implements AdminUserService { return map; } -// 获得此月第一天日期 + // 获得此月第一天日期 public LocalDateTime getFirstDayOfMonth() { // 获取当前日期 LocalDate today = LocalDate.now();