1、禅道#795问题修复,支持部门禁用的实时检查;2、新增用户、部门、组织删除和禁用时对对应用户的token移除;

This commit is contained in:
gaoqr
2024-11-29 17:04:52 +08:00
parent 0de7725788
commit 45482a5dbf
12 changed files with 246 additions and 42 deletions
@@ -1,2 +1,2 @@
com.cf.imes.framework.datapermission.config.ChenfengDataPermissionAutoConfiguration #com.cf.imes.framework.datapermission.config.ChenfengDataPermissionAutoConfiguration
com.cf.imes.framework.datapermission.config.ChenfengDeptDataPermissionAutoConfiguration #com.cf.imes.framework.datapermission.config.ChenfengDeptDataPermissionAutoConfiguration
@@ -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.organ.core.web.OrganContextWebFilter;
import com.cf.imes.framework.web.config.WebProperties; import com.cf.imes.framework.web.config.WebProperties;
import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler; 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.cf.imes.module.system.api.organ.OrganApi;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
@@ -42,8 +43,8 @@ import java.util.Objects;
public class ChenfengOrganAutoConfiguration { public class ChenfengOrganAutoConfiguration {
@Bean @Bean
public OrganFrameworkService tenantFrameworkService(OrganApi organApi) { public OrganFrameworkService tenantFrameworkService(OrganApi organApi, DeptApi deptApi) {
return new OrganFrameworkServiceImpl(organApi); return new OrganFrameworkServiceImpl(organApi, deptApi);
} }
// ========== AOP ========== // ========== AOP ==========
@@ -61,8 +61,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
throws ServletException, IOException { throws ServletException, IOException {
Long organId = WebFrameworkUtils.getOrganId(request); Long organId = WebFrameworkUtils.getOrganId(request);
// 1. 登陆的用户,校验是否有权限访问该组织,避免越权问题。 // 1. 登陆的用户,校验是否有权限访问该组织,避免越权问题。
Long deptId = null;
LoginUser user = SecurityFrameworkUtils.getLoginUser(); LoginUser user = SecurityFrameworkUtils.getLoginUser();
if (user != null) { if (user != null) {
deptId = user.getDeptId();
// 如果获取不到组织编号,则尝试使用登陆用户的组织编号 // 如果获取不到组织编号,则尝试使用登陆用户的组织编号
if (organId == null) { if (organId == null) {
organId = user.getOrganId(); organId = user.getOrganId();
@@ -89,7 +91,10 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
} }
// 3. 校验组织是合法,例如说被禁用、到期 // 3. 校验组织是合法,例如说被禁用、到期
try { try {
// 校验组织
organFrameworkService.validOrgan(organId); organFrameworkService.validOrgan(organId);
// 校验部门
organFrameworkService.validDept(deptId);
} catch (Throwable ex) { } catch (Throwable ex) {
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
// 组织失效返回401踢出系统 // 组织失效返回401踢出系统
@@ -23,4 +23,10 @@ public interface OrganFrameworkService {
*/ */
void validOrgan(Long id); void validOrgan(Long id);
/**
* 校验部门是否合法
*
* @param id
*/
void validDept(Long id);
} }
@@ -1,6 +1,7 @@
package com.cf.imes.framework.organ.core.service; package com.cf.imes.framework.organ.core.service;
import com.cf.imes.framework.common.util.cache.CacheUtils; 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.cf.imes.module.system.api.organ.OrganApi;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
@@ -20,6 +21,8 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService {
private final OrganApi organApi; private final OrganApi organApi;
private final DeptApi deptApi;
/** /**
* 针对 {@link #getOrganIds()} 的缓存 * 针对 {@link #getOrganIds()} 的缓存
*/ */
@@ -46,4 +49,8 @@ public class OrganFrameworkServiceImpl implements OrganFrameworkService {
organApi.validOrgan(id).checkError(); organApi.validOrgan(id).checkError();
} }
@Override
public void validDept(Long id) {
deptApi.validateDept(id);
}
} }
@@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection; import java.util.Collection;
@@ -37,6 +38,11 @@ public interface DeptApi {
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true) @Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids); CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/valid/{deptId}")
@Operation(summary = "校验部门是否合法")
@Parameter(name = "id", description = "部门编号", example = "1024", required = true)
CommonResult<Boolean> validateDept(@PathVariable("id") Long deptId);
/** /**
* 获得指定编号的部门 Map * 获得指定编号的部门 Map
* *
@@ -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_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_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_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 ========== // ========== 岗位模块 1-002-005-000 ==========
public static final ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); public static final ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在");
@@ -39,4 +39,9 @@ public class DeptApiImpl implements DeptApi {
return success(true); return success(true);
} }
@Override
public CommonResult<Boolean> validateDept(Long deptId) {
deptService.validDept(deptId);
return success(true);
}
} }
@@ -99,4 +99,10 @@ public interface DeptService {
*/ */
void validateDeptList(Collection<Long> ids); void validateDeptList(Collection<Long> ids);
/**
* 校验部门是否存在且有效(包括上级)
*
* @param deptId
*/
void validDept(Long deptId);
} }
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; 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.common.util.object.BeanUtils;
import com.cf.imes.framework.datapermission.core.annotation.DataPermission; import com.cf.imes.framework.datapermission.core.annotation.DataPermission;
import com.cf.imes.framework.security.core.LoginUser; 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.DeptListReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; 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.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.mysql.dept.DeptMapper;
import com.cf.imes.module.system.dal.redis.RedisKeyConstants; import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
import com.cf.imes.module.system.enums.ErrorCodeConstants; 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.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy; 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.stereotype.Service;
import org.springframework.validation.annotation.Validated; 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.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet; 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; import static com.cf.imes.module.system.enums.ErrorCodeConstants.DEPT_USER_OPER_NOT_ALLOW;
/** /**
@@ -47,6 +53,9 @@ public class DeptServiceImpl implements DeptService {
@Lazy // 延迟,避免循环依赖报错 @Lazy // 延迟,避免循环依赖报错
private AdminUserService userService; private AdminUserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override @Override
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
@@ -69,19 +78,26 @@ public class DeptServiceImpl implements DeptService {
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
public void updateDept(DeptSaveReqVO updateReqVO) { public void updateDept(DeptSaveReqVO updateReqVO) {
Long deptId = updateReqVO.getId();
if (updateReqVO.getParentId() == null) { if (updateReqVO.getParentId() == null) {
updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT); 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); DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
deptMapper.updateById(updateObj); deptMapper.updateById(updateObj);
if (CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) {
// 移除部门下用户的token
scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), deptId);
}
} }
@Override @Override
@@ -100,6 +116,35 @@ public class DeptServiceImpl implements DeptService {
deptMapper.deleteById(id); deptMapper.deleteById(id);
// 删除关联的用户 // 删除关联的用户
userService.deleteDeptUsers(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<String> matchKeys = new ArrayList<>();
Cursor<String> 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);
}
}
}
} }
@@ -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.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils; 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.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.object.BeanUtils;
import com.cf.imes.framework.common.util.pinyin.PinYinUtils; import com.cf.imes.framework.common.util.pinyin.PinYinUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; 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.controller.admin.user.vo.user.UserSaveReqVO;
import com.cf.imes.module.system.convert.organ.OrganConvert; 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.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.MenuDO;
import com.cf.imes.module.system.dal.dataobject.permission.RoleDO; import com.cf.imes.module.system.dal.dataobject.permission.RoleDO;
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO; 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.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -55,6 +60,7 @@ import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; 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 com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
@@ -93,6 +99,9 @@ public class OrganServiceImpl implements OrganService {
@Resource @Resource
private MachineLimitMapper machineLimitMapper; private MachineLimitMapper machineLimitMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override @Override
public List<Long> getOrganIdList() { public List<Long> getOrganIdList() {
@@ -205,12 +214,13 @@ public class OrganServiceImpl implements OrganService {
@Override @Override
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
public void updateOrgan(OrganSaveReqVO updateReqVO) { 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()); TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
if(!Objects.equals(tenant.getName(), updateReqVO.getName())) { if(!Objects.equals(tenant.getName(), updateReqVO.getName())) {
@@ -228,6 +238,10 @@ public class OrganServiceImpl implements OrganService {
if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) { if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
permissionService.flushCacheWhenTenantPackageChange(List.of(tenant.getId())); 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) { private void validTenantNameDuplicate(String name, Long id) {
@@ -297,6 +311,35 @@ public class OrganServiceImpl implements OrganService {
organMapper.deleteById(id); organMapper.deleteById(id);
// 删除关联的用户 // 删除关联的用户
userService.deleteOrgUsers(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<String> matchKeys = new ArrayList<>();
Cursor<String> 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;
}
}
}
} }
/** /**
@@ -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.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils; 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.monitor.TracerUtils;
import com.cf.imes.framework.common.util.object.BeanUtils; 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.pinyin.PinYinUtils;
import com.cf.imes.framework.common.util.servlet.ServletUtils; 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.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX; import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.cf.imes.framework.organ.core.aop.OrganIgnore; 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.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.DeptDO;
import com.cf.imes.module.system.dal.dataobject.dept.UserPostDO; 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.permission.UserRoleDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.dal.mysql.dept.UserPostMapper; 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.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; 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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; 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.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.convertList;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet; 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_ME_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.USER_OPERATE_SELF_STATUS_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 @Resource
private LoginLogService loginLogService; private LoginLogService loginLogService;
@Resource
private StringRedisTemplate stringRedisTemplate;
// 定义密码校验的正则表达式 // 定义密码校验的正则表达式
private static final String LENGTH_PATTERN = ".{8,}"; private static final String LENGTH_PATTERN = ".{8,}";
@@ -162,10 +169,11 @@ public class AdminUserServiceImpl implements AdminUserService {
public void updateUser(UserSaveReqVO updateReqVO) { 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(); 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); updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds(), null, organId);
// 更新用户 // 更新用户
AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
@@ -192,6 +200,37 @@ public class AdminUserServiceImpl implements AdminUserService {
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
// 更新岗位 // 更新岗位
updateUserPost(updateReqVO, 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<String> matchKeys = new ArrayList<>();
Cursor<String> 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) { private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
@@ -313,6 +352,10 @@ public class AdminUserServiceImpl implements AdminUserService {
updateObj.setId(id); updateObj.setId(id);
updateObj.setStatus(status); updateObj.setStatus(status);
userMapper.updateById(updateObj); userMapper.updateById(updateObj);
if (CommonStatusEnum.DISABLE.getStatus().equals(status)) {
// 移除用户token
scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id);
}
} }
@Override @Override
@@ -329,6 +372,8 @@ public class AdminUserServiceImpl implements AdminUserService {
permissionService.processUserDeleted(id); permissionService.processUserDeleted(id);
// 删除用户岗位 // 删除用户岗位
userPostMapper.deleteByUserId(id); userPostMapper.deleteByUserId(id);
// 移除用户token
scanAndCompareUserAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id);
} }
} }
@@ -479,23 +524,21 @@ public class AdminUserServiceImpl implements AdminUserService {
return deptIds; return deptIds;
} }
private void validateUserForCreateOrUpdate(Long id, String username, String email, private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String email,
Long deptId, Set<Long> postIds, String password, Long organId) { Long deptId, Set<Long> postIds, String password, Long organId) {
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
DataPermissionUtils.executeIgnore(() -> {
// 校验用户的密码是否符合规则 // 校验用户的密码是否符合规则
if (password != null) { if (password != null) {
validatePassword(password); validatePassword(password);
} }
// 校验用户存在 // 校验用户存在
validateUserExists(id); AdminUserDO adminUserDO = validateUserExists(id);
// 校验用户名唯一 // 校验用户名唯一
validateUsernameUnique(id, username, organId); validateUsernameUnique(id, username, organId);
// 校验部门处于开启状态 // 校验部门处于开启状态
deptService.validateDeptList(CollectionUtils.singleton(deptId)); deptService.validDept(deptId);
// 校验岗位处于开启状态 // 校验岗位处于开启状态
postService.validatePostList(postIds); postService.validatePostList(postIds);
}); return adminUserDO;
} }
@VisibleForTesting @VisibleForTesting
@@ -529,14 +572,15 @@ public class AdminUserServiceImpl implements AdminUserService {
} }
@VisibleForTesting @VisibleForTesting
void validateUserExists(Long id) { AdminUserDO validateUserExists(Long id) {
if (id == null) { if (id == null) {
return; return null;
} }
AdminUserDO user = userMapper.selectById(id); AdminUserDO user = userMapper.selectById(id);
if (user == null) { if (user == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS); throw ServiceExceptionUtil.exception(ErrorCodeConstants.USER_NOT_EXISTS);
} }
return user;
} }
@VisibleForTesting @VisibleForTesting