feat: 数据权限服务.

This commit is contained in:
lijiahang
2023-11-22 18:51:56 +08:00
parent 675c88a625
commit ab72297706
23 changed files with 902 additions and 158 deletions

View File

@@ -12,6 +12,7 @@ import com.orion.ops.module.infra.define.operator.SystemUserOperatorType;
import com.orion.ops.module.infra.entity.request.user.*;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.service.SystemUserManagementService;
import com.orion.ops.module.infra.service.SystemUserRoleService;
import com.orion.ops.module.infra.service.SystemUserService;
import io.swagger.v3.oas.annotations.Operation;
@@ -47,6 +48,9 @@ public class SystemUserController {
@Resource
private SystemUserRoleService systemUserRoleService;
@Resource
private SystemUserManagementService systemUserManagementService;
@OperatorLog(SystemUserOperatorType.CREATE)
@PostMapping("/create")
@Operation(summary = "创建用户")
@@ -118,7 +122,7 @@ public class SystemUserController {
@Operation(summary = "查询用户的角色id")
@PreAuthorize("@ss.hasPermission('infra:system-user:query')")
public List<Long> getUserRoleIdList(@RequestParam("userId") Long userId) {
return systemUserRoleService.getUserRoleIdList(userId);
return systemUserRoleService.getRoleIdListByUserId(userId);
}
@IgnoreLog(IgnoreLogMode.RET)
@@ -143,7 +147,7 @@ public class SystemUserController {
@Operation(summary = "获取用户会话列表")
@PreAuthorize("@ss.hasPermission('infra:system-user:query-session')")
public List<UserSessionVO> getUserSessionList(@RequestParam("id") Long id) {
return systemUserService.getUserSessionList(id);
return systemUserManagementService.getUserSessionList(id);
}
@OperatorLog(SystemUserOperatorType.OFFLINE)
@@ -151,7 +155,7 @@ public class SystemUserController {
@Operation(summary = "下线用户会话")
@PreAuthorize("@ss.hasPermission('infra:system-user:offline-session')")
public HttpWrapper<?> offlineUserSession(@Validated @RequestBody UserSessionOfflineRequest request) {
systemUserService.offlineUserSession(request);
systemUserManagementService.offlineUserSession(request);
return HttpWrapper.ok();
}

View File

@@ -0,0 +1,29 @@
package com.orion.ops.module.infra.convert;
import com.orion.ops.module.infra.entity.domain.*;
import com.orion.ops.module.infra.entity.vo.*;
import com.orion.ops.module.infra.entity.request.data.*;
import com.orion.ops.module.infra.convert.*;
import com.orion.ops.module.infra.define.operator.*;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 数据权限 内部对象转换器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Mapper
public interface DataPermissionConvert {
DataPermissionConvert MAPPER = Mappers.getMapper(DataPermissionConvert.class);
DataPermissionDO to(DataPermissionCreateRequest request);
DataPermissionDO to(DataPermissionUpdateRequest request);
}

View File

@@ -0,0 +1,33 @@
package com.orion.ops.module.infra.dao;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.ops.framework.mybatis.core.mapper.IMapper;
import com.orion.ops.module.infra.entity.domain.DataPermissionDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据权限 Mapper 接口
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Mapper
public interface DataPermissionDAO extends IMapper<DataPermissionDO> {
/**
* 获取查询条件
*
* @param entity entity
* @return 查询条件
*/
default LambdaQueryWrapper<DataPermissionDO> queryCondition(DataPermissionDO entity) {
return this.wrapper()
.eq(DataPermissionDO::getId, entity.getId())
.eq(DataPermissionDO::getUserId, entity.getUserId())
.eq(DataPermissionDO::getRoleId, entity.getRoleId())
.eq(DataPermissionDO::getRelId, entity.getRelId())
.eq(DataPermissionDO::getType, entity.getType());
}
}

View File

@@ -48,6 +48,22 @@ public interface SystemUserRoleDAO extends IMapper<SystemUserRoleDO> {
.collect(Collectors.toList());
}
/**
* 查询角色的全部 userId
*
* @param roleId roleId
* @return userId
*/
default List<Long> selectUserIdByRoleId(List<Long> roleId) {
LambdaQueryWrapper<SystemUserRoleDO> wrapper = this.wrapper()
.select(SystemUserRoleDO::getUserId)
.in(SystemUserRoleDO::getRoleId, roleId);
return this.selectList(wrapper).stream()
.map(SystemUserRoleDO::getUserId)
.distinct()
.collect(Collectors.toList());
}
/**
* 通过 userId 删除
*

View File

@@ -0,0 +1,26 @@
package com.orion.ops.module.infra.define.cache;
import com.orion.lang.define.cache.key.CacheKeyBuilder;
import com.orion.lang.define.cache.key.CacheKeyDefine;
import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
import java.util.concurrent.TimeUnit;
/**
* 数据权限缓存 key
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/11/21 11:29
*/
public interface DataPermissionCacheKeyDefine {
CacheKeyDefine DATA_PERMISSION_USER = new CacheKeyBuilder()
.key("data:perm-user:{}:{}")
.desc("用户所有数据权限 ${type} ${userId}")
.type(Long.class)
.struct(RedisCacheStruct.LIST)
.timeout(1, TimeUnit.DAYS)
.build();
}

View File

@@ -1,34 +0,0 @@
package com.orion.ops.module.infra.define.operator;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
/**
* 数据分组 操作日志类型
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
@Module("infra:data-group")
public class DataGroupOperatorType extends InitializingOperatorTypes {
public static final String CREATE = "data-group:create";
public static final String UPDATE = "data-group:update";
public static final String DELETE = "data-group:delete";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(L, CREATE, "创建数据分组"),
new OperatorType(M, UPDATE, "更新数据分组"),
new OperatorType(H, DELETE, "删除数据分组"),
};
}
}

View File

@@ -1,34 +0,0 @@
package com.orion.ops.module.infra.define.operator;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
/**
* 数据分组关联 操作日志类型
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-7 18:44
*/
@Module("infra:data-group-rel")
public class DataGroupRelOperatorType extends InitializingOperatorTypes {
public static final String CREATE = "data-group-rel:create";
public static final String UPDATE = "data-group-rel:update";
public static final String DELETE = "data-group-rel:delete";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(L, CREATE, "创建数据分组关联"),
new OperatorType(M, UPDATE, "更新数据分组关联"),
new OperatorType(H, DELETE, "删除数据分组关联"),
};
}
}

View File

@@ -0,0 +1,49 @@
package com.orion.ops.module.infra.entity.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.orion.ops.framework.mybatis.core.domain.BaseDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.math.*;
/**
* 数据权限 实体对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName(value = "data_permission", autoResultMap = true)
@Schema(name = "DataPermissionDO", description = "数据权限 实体对象")
public class DataPermissionDO extends BaseDO {
private static final long serialVersionUID = 1L;
@Schema(description = "id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "用户id")
@TableField("user_id")
private Long userId;
@Schema(description = "角色id")
@TableField("role_id")
private Long roleId;
@Schema(description = "引用id")
@TableField("rel_id")
private Long relId;
@Schema(description = "数据类型")
@TableField("type")
private String type;
}

View File

@@ -0,0 +1,40 @@
package com.orion.ops.module.infra.entity.request.data;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 数据权限 创建请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataPermissionCreateRequest", description = "数据权限 创建请求对象")
public class DataPermissionCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "角色id")
private Long roleId;
@Schema(description = "引用id")
private List<Long> relIdList;
@Schema(description = "数据类型")
private String type;
}

View File

@@ -0,0 +1,40 @@
package com.orion.ops.module.infra.entity.request.data;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 数据权限 更新请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataPermissionUpdateRequest", description = "数据权限 更新请求对象")
public class DataPermissionUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "角色id")
private Long roleId;
@Schema(description = "引用id")
private List<Long> relIdList;
@Schema(description = "数据类型")
private String type;
}

View File

@@ -0,0 +1,104 @@
package com.orion.ops.module.infra.service;
import com.orion.ops.module.infra.entity.request.data.DataPermissionCreateRequest;
import com.orion.ops.module.infra.entity.request.data.DataPermissionUpdateRequest;
import java.util.List;
/**
* 数据权限 服务类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
public interface DataPermissionService {
/**
* 添加数据权限
*
* @param request request
*/
void addDataPermission(DataPermissionCreateRequest request);
/**
* 更新数据权限
*
* @param request request
*/
void updateDataPermission(DataPermissionUpdateRequest request);
/**
* 通过 userId 查询 (不包含角色 不走缓存)
*
* @param type type
* @param userId userId
* @return relId
*/
List<Long> getRelIdListByUserId(String type, Long userId);
/**
* 通过 roleId 查询 不走缓存
*
* @param type type
* @param roleId roleId
* @return relId
*/
List<Long> getRelIdListByRoleId(String type, Long roleId);
/**
* 通过 userId 查询 (包含角色 走缓存)
*
* @param type type
* @param userId userId
* @return relId
*/
List<Long> getAllowRelIdList(String type, Long userId);
/**
* 通过 relId 删除
*
* @param type type
* @param relId relId
* @return effect
*/
int deleteByRelId(String type, Long relId);
/**
* 通过 userId 删除
*
* @param userId userId
* @return effect
*/
int deleteByUserId(Long userId);
/**
* 通过 roleId 删除
*
* @param roleId roleId
* @return effect
*/
int deleteByRoleId(Long roleId);
/**
* 清空角色缓存
*
* @param roleId roleId
*/
void clearRoleCache(Long roleId);
/**
* 清空用户缓存
*
* @param userId userId
*/
void clearUserCache(Long userId);
/**
* 清空用户缓存
*
* @param userIdList userIdList
*/
void clearUserCache(List<Long> userIdList);
}

View File

@@ -0,0 +1,32 @@
package com.orion.ops.module.infra.service;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import java.util.List;
/**
* 用户管理服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/11/22 18:17
*/
public interface SystemUserManagementService {
/**
* 获取用户会话列表
*
* @param userId userId
* @return 回话列表
*/
List<UserSessionVO> getUserSessionList(Long userId);
/**
* 下线用户会话
*
* @param request request
*/
void offlineUserSession(UserSessionOfflineRequest request);
}

View File

@@ -14,12 +14,20 @@ import java.util.List;
public interface SystemUserRoleService {
/**
* 查询用户 roleId
* 通过 userId 查询 roleId
*
* @param userId userId
* @return roleId
*/
List<Long> getUserRoleIdList(Long userId);
List<Long> getRoleIdListByUserId(Long userId);
/**
* 通过 roleCode 查询 userId
*
* @param roleCode roleCode
* @return userId
*/
List<Long> getUserIdListByRoleCode(String roleCode);
/**
* 删除用户角色

View File

@@ -3,7 +3,6 @@ package com.orion.ops.module.infra.service;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.module.infra.entity.request.user.*;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import java.util.List;
@@ -87,18 +86,11 @@ public interface SystemUserService {
void resetPassword(UserResetPasswordRequest request);
/**
* 获取用户会话列表
* 检测用户是否是为管理员
*
* @param userId userId
* @return 回话列表
* @return 是否为管理员
*/
List<UserSessionVO> getUserSessionList(Long userId);
/**
* 下线用户会话
*
* @param request request
*/
void offlineUserSession(UserSessionOfflineRequest request);
boolean isAdminUser(Long userId);
}

View File

@@ -8,6 +8,7 @@ import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.crypto.Signatures;
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.security.UserRole;
@@ -130,8 +131,13 @@ public class AuthenticationServiceImpl implements AuthenticationService {
if (userInfoCache != null) {
return JSON.parseObject(userInfoCache, LoginUser.class);
}
// 设置缓存并返回
return this.setUserCache(systemUserDAO.selectById(id));
// 查询用户信息
SystemUserDO user = systemUserDAO.selectById(id);
if (user == null) {
throw Exceptions.httpWrapper(ErrorCode.UNAUTHORIZED);
}
// 设置用户缓存
return this.setUserCache(user);
}
@Override

View File

@@ -0,0 +1,310 @@
package com.orion.ops.module.infra.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.mybatis.core.query.Conditions;
import com.orion.ops.framework.redis.core.utils.RedisLists;
import com.orion.ops.framework.redis.core.utils.RedisUtils;
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
import com.orion.ops.module.infra.dao.DataPermissionDAO;
import com.orion.ops.module.infra.dao.SystemUserRoleDAO;
import com.orion.ops.module.infra.define.cache.DataPermissionCacheKeyDefine;
import com.orion.ops.module.infra.entity.domain.DataPermissionDO;
import com.orion.ops.module.infra.entity.request.data.DataPermissionCreateRequest;
import com.orion.ops.module.infra.entity.request.data.DataPermissionUpdateRequest;
import com.orion.ops.module.infra.service.DataPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 数据权限 服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-11-21 10:32
*/
@Slf4j
@Service
public class DataPermissionServiceImpl implements DataPermissionService {
@Resource
private DataPermissionDAO dataPermissionDAO;
@Resource
private SystemUserRoleDAO systemUserRoleDAO;
@Override
@Transactional(rollbackFor = Exception.class)
public void addDataPermission(DataPermissionCreateRequest request) {
Long userId = request.getUserId();
Long roleId = request.getRoleId();
String type = request.getType();
// 查询
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.wrapper()
.eq(DataPermissionDO::getUserId, userId)
.eq(DataPermissionDO::getRoleId, roleId)
.eq(DataPermissionDO::getType, type);
List<Long> beforeRelIdList = dataPermissionDAO.selectList(wrapper)
.stream()
.map(DataPermissionDO::getRelId)
.distinct()
.collect(Collectors.toList());
// 新增
List<DataPermissionDO> records = request.getRelIdList()
.stream()
.distinct()
.filter(s -> !beforeRelIdList.contains(s))
.map(s -> DataPermissionDO.builder()
.type(type)
.userId(userId)
.roleId(roleId)
.relId(s)
.build())
.collect(Collectors.toList());
dataPermissionDAO.insertBatch(records);
// 删除缓存
this.deleteCache(type, userId, roleId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateDataPermission(DataPermissionUpdateRequest request) {
Long userId = request.getUserId();
Long roleId = request.getRoleId();
String type = request.getType();
// 删除
if (Lists.isEmpty(request.getRelIdList())) {
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.wrapper()
.eq(DataPermissionDO::getUserId, userId)
.eq(DataPermissionDO::getRoleId, roleId)
.eq(DataPermissionDO::getType, type);
dataPermissionDAO.delete(wrapper);
return;
}
// 新增
List<DataPermissionDO> records = request.getRelIdList()
.stream()
.distinct()
.map(s -> DataPermissionDO.builder()
.type(type)
.userId(userId)
.roleId(roleId)
.relId(s)
.build())
.collect(Collectors.toList());
dataPermissionDAO.insertBatch(records);
// 删除缓存
this.deleteCache(type, userId, roleId);
}
@Override
public List<Long> getRelIdListByUserId(String type, Long userId) {
return dataPermissionDAO.of()
.createWrapper()
.eq(DataPermissionDO::getType, type)
.eq(DataPermissionDO::getUserId, userId)
.then()
.stream()
.map(DataPermissionDO::getRelId)
.distinct()
.collect(Collectors.toList());
}
@Override
public List<Long> getRelIdListByRoleId(String type, Long roleId) {
// 查询数据库
return dataPermissionDAO.of()
.createWrapper()
.eq(DataPermissionDO::getType, type)
.eq(DataPermissionDO::getRoleId, roleId)
.then()
.stream()
.map(DataPermissionDO::getRelId)
.distinct()
.collect(Collectors.toList());
}
@Override
public List<Long> getAllowRelIdList(String type, Long userId) {
String cacheKey = DataPermissionCacheKeyDefine.DATA_PERMISSION_USER.format(type, userId);
// 获取缓存
List<Long> list = RedisLists.range(cacheKey, Long::valueOf);
if (list.isEmpty()) {
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.lambda()
.eq(DataPermissionDO::getType, type)
.eq(DataPermissionDO::getUserId, userId);
// 查询用户角色
List<Long> roleIdList = systemUserRoleDAO.selectRoleIdByUserId(userId);
if (!roleIdList.isEmpty()) {
wrapper.or().in(DataPermissionDO::getRoleId, roleIdList);
}
// 查询数据库
list = dataPermissionDAO.of()
.wrapper(wrapper)
.stream()
.map(DataPermissionDO::getRelId)
.distinct()
.collect(Collectors.toList());
// 设置屏障 防止穿透
CacheBarriers.LONG.check(list);
// 设置缓存
RedisLists.pushAll(cacheKey, DataPermissionCacheKeyDefine.DATA_PERMISSION_USER, list, String::valueOf);
}
// 删除屏障
CacheBarriers.LONG.remove(list);
return list.stream()
.distinct()
.collect(Collectors.toList());
}
@Override
public int deleteByRelId(String type, Long relId) {
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.wrapper()
.eq(DataPermissionDO::getType, type)
.eq(DataPermissionDO::getRelId, relId);
// 查询
List<DataPermissionDO> rows = dataPermissionDAO.selectList(wrapper);
// 删除
int effect = dataPermissionDAO.delete(wrapper);
// 删除缓存
Function<Function<DataPermissionDO, Long>, List<Long>> mapper =
f -> rows.stream()
.map(f)
.distinct()
.filter(Objects::nonNull)
.collect(Collectors.toList());
List<Long> userIdList = mapper.apply(DataPermissionDO::getUserId);
List<Long> roleIdList = mapper.apply(DataPermissionDO::getRoleId);
this.deleteCache(Lists.singleton(type), userIdList, roleIdList);
return effect;
}
@Override
public int deleteByUserId(Long userId) {
LambdaQueryWrapper<DataPermissionDO> wrapper = Conditions.eq(DataPermissionDO::getUserId, userId);
// 查询
List<String> typeList = dataPermissionDAO.of()
.wrapper(wrapper)
.stream()
.map(DataPermissionDO::getType)
.distinct()
.collect(Collectors.toList());
// 删除
int effect = dataPermissionDAO.delete(wrapper);
// 删除缓存
this.deleteCache(typeList, Lists.singleton(userId), null);
return effect;
}
@Override
public int deleteByRoleId(Long roleId) {
LambdaQueryWrapper<DataPermissionDO> wrapper = Conditions.eq(DataPermissionDO::getRoleId, roleId);
// 查询
List<String> typeList = dataPermissionDAO.of()
.wrapper(wrapper)
.stream()
.map(DataPermissionDO::getType)
.distinct()
.collect(Collectors.toList());
// 删除
int effect = dataPermissionDAO.delete(wrapper);
// 删除缓存
this.deleteCache(typeList, null, Lists.singleton(roleId));
return effect;
}
@Override
public void clearRoleCache(Long roleId) {
// 查询角色下的用户
List<Long> userIdList = systemUserRoleDAO.selectUserIdByRoleId(roleId);
if (userIdList.isEmpty()) {
return;
}
this.clearUserCache(userIdList);
}
@Override
public void clearUserCache(Long userId) {
this.clearUserCache(Lists.singleton(userId));
}
@Override
public void clearUserCache(List<Long> userIdList) {
// 构建 key 匹配
List<String> keyPatterns = userIdList.stream()
.distinct()
.map(s -> DataPermissionCacheKeyDefine.DATA_PERMISSION_USER.format("*", s))
.collect(Collectors.toList());
// 扫描所有 key
List<String> deleteKeys = keyPatterns.stream()
.map(RedisUtils::scanKeys)
.flatMap(Collection::stream)
.collect(Collectors.toList());
// 删除 key
if (!deleteKeys.isEmpty()) {
RedisUtils.delete(deleteKeys);
}
}
/**
* 删除缓存
*
* @param type type
* @param userId userId
* @param roleId roleId
*/
private void deleteCache(String type, Long userId, Long roleId) {
List<Long> userIdList = new ArrayList<>();
if (userId != null) {
userIdList.add(userId);
}
// 查询角色的权限
List<Long> roleUserIdList = systemUserRoleDAO.selectUserIdByRoleId(roleId);
userIdList.addAll(roleUserIdList);
// 删除缓存
if (!userIdList.isEmpty()) {
List<String> keys = userIdList.stream()
.map(s -> DataPermissionCacheKeyDefine.DATA_PERMISSION_USER.format(type, s))
.collect(Collectors.toList());
RedisUtils.delete(keys);
}
}
/**
* 删除缓存
*
* @param typeList typeList
* @param userIdList userIdList
* @param roleIdList roleIdList
*/
private void deleteCache(List<String> typeList, List<Long> userIdList, List<Long> roleIdList) {
Set<Long> deleteUserIdList = new HashSet<>(4);
if (!Lists.isEmpty(userIdList)) {
deleteUserIdList.addAll(userIdList);
}
// 查询角色的用户列表
if (!Lists.isEmpty(roleIdList)) {
List<Long> roleUserIdList = systemUserRoleDAO.selectUserIdByRoleId(roleIdList);
deleteUserIdList.addAll(roleUserIdList);
}
if (deleteUserIdList.isEmpty()) {
return;
}
// 删除缓存
List<String> keys = new ArrayList<>();
for (String type : typeList) {
userIdList.stream()
.filter(Objects::nonNull)
.map(s -> DataPermissionCacheKeyDefine.DATA_PERMISSION_USER.format(type, s))
.forEach(keys::add);
}
RedisLists.delete(keys);
}
}

View File

@@ -18,6 +18,7 @@ import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.service.MineService;
import com.orion.ops.module.infra.service.OperatorLogService;
import com.orion.ops.module.infra.service.SystemUserManagementService;
import com.orion.ops.module.infra.service.SystemUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -39,6 +40,9 @@ public class MineServiceImpl implements MineService {
@Resource
private SystemUserService systemUserService;
@Resource
private SystemUserManagementService systemUserManagementService;
@Resource
private OperatorLogService operatorLogService;
@@ -80,13 +84,13 @@ public class MineServiceImpl implements MineService {
@Override
public List<UserSessionVO> getCurrentUserSessionList() {
return systemUserService.getUserSessionList(SecurityUtils.getLoginUserId());
return systemUserManagementService.getUserSessionList(SecurityUtils.getLoginUserId());
}
@Override
public void offlineCurrentUserSession(UserSessionOfflineRequest request) {
request.setUserId(SecurityUtils.getLoginUserId());
systemUserService.offlineUserSession(request);
systemUserManagementService.offlineUserSession(request);
}
@Override

View File

@@ -19,6 +19,7 @@ import com.orion.ops.module.infra.entity.request.role.SystemRoleStatusRequest;
import com.orion.ops.module.infra.entity.request.role.SystemRoleUpdateRequest;
import com.orion.ops.module.infra.entity.vo.SystemRoleVO;
import com.orion.ops.module.infra.enums.RoleStatusEnum;
import com.orion.ops.module.infra.service.DataPermissionService;
import com.orion.ops.module.infra.service.PermissionService;
import com.orion.ops.module.infra.service.SystemRoleService;
import com.orion.ops.module.infra.service.SystemUserRoleService;
@@ -55,6 +56,9 @@ public class SystemRoleServiceImpl implements SystemRoleService {
@Resource
private SystemUserRoleService systemUserRoleService;
@Resource
private DataPermissionService dataPermissionService;
@Override
public Long createSystemRole(SystemRoleCreateRequest request) {
// 转换
@@ -111,9 +115,11 @@ public class SystemRoleServiceImpl implements SystemRoleService {
// 更新
int effect = systemRoleDAO.updateById(updateRecord);
log.info("SystemRoleService-updateRoleStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
// 修改缓存状态
// 修改本地缓存状态
SystemRoleDO roleCache = permissionService.getRoleCache().get(id);
roleCache.setStatus(status);
// 删除数据权限缓存
dataPermissionService.clearRoleCache(id);
return effect;
}
@@ -178,6 +184,8 @@ public class SystemRoleServiceImpl implements SystemRoleService {
permissionService.getRoleMenuCache().remove(id);
// 删除用户缓存中的角色
systemUserRoleService.deleteUserCacheRoleAsync(id, userIdList);
// 删除数据权限缓存
dataPermissionService.clearUserCache(userIdList);
return effect;
}

View File

@@ -0,0 +1,99 @@
package com.orion.ops.module.infra.service.impl;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Requests;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisStrings;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.infra.dao.SystemUserDAO;
import com.orion.ops.module.infra.define.cache.UserCacheKeyDefine;
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.entity.dto.LoginTokenIdentityDTO;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
import com.orion.ops.module.infra.service.SystemUserManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 用户管理 服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/11/22 18:17
*/
@Slf4j
@Service
public class SystemUserManagementServiceImpl implements SystemUserManagementService {
@Resource
private SystemUserDAO systemUserDAO;
@Override
public List<UserSessionVO> getUserSessionList(Long userId) {
// 扫描缓存
Set<String> keys = RedisStrings.scanKeys(UserCacheKeyDefine.LOGIN_TOKEN.format(userId, "*"));
if (Lists.isEmpty(keys)) {
return Lists.empty();
}
// 查询缓存
List<LoginTokenDTO> tokens = RedisStrings.getJsonList(keys, UserCacheKeyDefine.LOGIN_TOKEN);
if (Lists.isEmpty(tokens)) {
return Lists.empty();
}
final boolean isCurrentUser = userId.equals(SecurityUtils.getLoginUserId());
// 返回
return tokens.stream()
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getStatus()))
.map(LoginTokenDTO::getOrigin)
.map(s -> UserSessionVO.builder()
.current(isCurrentUser && s.getLoginTime().equals(SecurityUtils.getLoginTimestamp()))
.address(s.getAddress())
.location(s.getLocation())
.userAgent(s.getUserAgent())
.loginTime(new Date(s.getLoginTime()))
.build())
.sorted(Comparator.comparing(UserSessionVO::getCurrent).reversed()
.thenComparing(Comparator.comparing(UserSessionVO::getLoginTime).reversed()))
.collect(Collectors.toList());
}
@Override
public void offlineUserSession(UserSessionOfflineRequest request) {
Long userId = Valid.notNull(request.getUserId());
Long timestamp = request.getTimestamp();
log.info("SystemUserManagementService offlineUserSession userId: {}, timestamp: {}", userId, timestamp);
// 查询用户
SystemUserDO user = systemUserDAO.selectById(userId);
Valid.notNull(user, ErrorMessage.USER_ABSENT);
// 添加日志参数
OperatorLogs.add(OperatorLogs.USERNAME, user.getUsername());
// 删除刷新缓存
RedisStrings.delete(UserCacheKeyDefine.LOGIN_REFRESH.format(userId, request.getTimestamp()));
// 查询并且覆盖 token
String tokenKey = UserCacheKeyDefine.LOGIN_TOKEN.format(userId, timestamp);
LoginTokenDTO tokenInfo = RedisStrings.getJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN);
if (tokenInfo != null) {
tokenInfo.setStatus(LoginTokenStatusEnum.SESSION_OFFLINE.getStatus());
LoginTokenIdentityDTO override = new LoginTokenIdentityDTO();
override.setLoginTime(System.currentTimeMillis());
// 设置请求信息
Requests.fillIdentity(override);
tokenInfo.setOverride(override);
// 更新 token
RedisStrings.setJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN, tokenInfo);
}
}
}

View File

@@ -15,6 +15,7 @@ import com.orion.ops.module.infra.entity.domain.SystemRoleDO;
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
import com.orion.ops.module.infra.entity.domain.SystemUserRoleDO;
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRoleRequest;
import com.orion.ops.module.infra.service.DataPermissionService;
import com.orion.ops.module.infra.service.SystemUserRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
@@ -45,11 +46,28 @@ public class SystemUserRoleServiceImpl implements SystemUserRoleService {
@Resource
private SystemUserRoleDAO systemUserRoleDAO;
@Resource
private DataPermissionService dataPermissionService;
@Override
public List<Long> getUserRoleIdList(Long userId) {
public List<Long> getRoleIdListByUserId(Long userId) {
return systemUserRoleDAO.selectRoleIdByUserId(userId);
}
@Override
public List<Long> getUserIdListByRoleCode(String roleCode) {
Long roleId = systemRoleDAO.of()
.createWrapper()
.eq(SystemRoleDO::getCode, roleCode)
.then()
.getOne(SystemRoleDO::getId);
if (roleId == null) {
return Lists.empty();
}
// 查询用户列表
return systemUserRoleDAO.selectUserIdByRoleId(roleId);
}
@Override
public Integer deleteUserRoles(SystemUserUpdateRoleRequest request) {
Long userId = request.getId();
@@ -60,10 +78,12 @@ public class SystemUserRoleServiceImpl implements SystemUserRoleService {
OperatorLogs.add(OperatorLogs.USERNAME, user.getUsername());
// 删除用户关联
int effect = systemUserRoleDAO.deleteByUserId(userId);
// 更新缓存中的角色
// 更新用户缓存中的角色
RedisStrings.<LoginUser>processSetJson(UserCacheKeyDefine.USER_INFO, s -> {
s.setRoles(Lists.empty());
}, userId);
// 清除数据权限缓存
dataPermissionService.clearUserCache(userId);
return effect;
}
@@ -107,6 +127,8 @@ public class SystemUserRoleServiceImpl implements SystemUserRoleService {
.collect(Collectors.toList());
s.setRoles(roles);
}, userId);
// 清除数据权限缓存
dataPermissionService.clearUserCache(userId);
return effect;
}

View File

@@ -3,13 +3,11 @@ package com.orion.ops.module.infra.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.crypto.Signatures;
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.utils.Requests;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisMaps;
import com.orion.ops.framework.redis.core.utils.RedisStrings;
@@ -18,31 +16,25 @@ import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.infra.convert.SystemUserConvert;
import com.orion.ops.module.infra.dao.OperatorLogDAO;
import com.orion.ops.module.infra.dao.SystemRoleDAO;
import com.orion.ops.module.infra.dao.SystemUserDAO;
import com.orion.ops.module.infra.dao.SystemUserRoleDAO;
import com.orion.ops.module.infra.define.RoleDefine;
import com.orion.ops.module.infra.define.cache.TipsCacheKeyDefine;
import com.orion.ops.module.infra.define.cache.UserCacheKeyDefine;
import com.orion.ops.module.infra.entity.domain.SystemRoleDO;
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.entity.dto.LoginTokenIdentityDTO;
import com.orion.ops.module.infra.entity.dto.UserInfoDTO;
import com.orion.ops.module.infra.entity.request.user.*;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
import com.orion.ops.module.infra.enums.UserStatusEnum;
import com.orion.ops.module.infra.service.AuthenticationService;
import com.orion.ops.module.infra.service.FavoriteService;
import com.orion.ops.module.infra.service.PreferenceService;
import com.orion.ops.module.infra.service.SystemUserService;
import com.orion.ops.module.infra.service.*;
import com.orion.spring.SpringHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -64,6 +56,9 @@ public class SystemUserServiceImpl implements SystemUserService {
@Resource
private SystemUserRoleDAO systemUserRoleDAO;
@Resource
private SystemRoleDAO systemRoleDAO;
@Resource
private OperatorLogDAO operatorLogDAO;
@@ -73,6 +68,9 @@ public class SystemUserServiceImpl implements SystemUserService {
@Resource
private PreferenceService preferenceService;
@Resource
private DataPermissionService dataPermissionService;
@Override
public Long createSystemUser(SystemUserCreateRequest request) {
// 转换
@@ -206,6 +204,15 @@ public class SystemUserServiceImpl implements SystemUserService {
// 删除用户
int effect = systemUserDAO.deleteById(id);
log.info("SystemUserService-deleteSystemUserById id: {}, effect: {}", id, effect);
// 删除用户信息缓存
RedisUtils.delete(UserCacheKeyDefine.USER_INFO.format(id));
// 删除 token 缓存
RedisUtils.scanKeysDelete(
// 登录 token
UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*"),
// 刷新 token
UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*")
);
// 异步删除额外信息
SpringHolder.getBean(SystemUserService.class).deleteSystemUserRelAsync(id, record.getUsername());
return effect;
@@ -217,10 +224,8 @@ public class SystemUserServiceImpl implements SystemUserService {
log.info("SystemUserService-deleteSystemUserRel id: {}", id);
// 删除用户列表缓存
RedisMaps.delete(UserCacheKeyDefine.USER_LIST, id);
// 删除用户缓存 需要扫描的 key 让其自动过期
// 删除用户缓存 其他的 key 让其自动过期
RedisUtils.delete(
// 用户缓存
UserCacheKeyDefine.USER_INFO.format(id),
// 登录失败次数
UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(username),
// 用户提示
@@ -234,6 +239,8 @@ public class SystemUserServiceImpl implements SystemUserService {
favoriteService.deleteFavoriteByUserId(id);
// 删除用户偏好
preferenceService.deletePreferenceByUserId(id);
// 删除用户数据权限
dataPermissionService.deleteByUserId(id);
}
@Override
@@ -269,58 +276,17 @@ public class SystemUserServiceImpl implements SystemUserService {
}
@Override
public List<UserSessionVO> getUserSessionList(Long userId) {
// 扫描缓存
Set<String> keys = RedisStrings.scanKeys(UserCacheKeyDefine.LOGIN_TOKEN.format(userId, "*"));
if (Lists.isEmpty(keys)) {
return Lists.empty();
}
// 查询缓存
List<LoginTokenDTO> tokens = RedisStrings.getJsonList(keys, UserCacheKeyDefine.LOGIN_TOKEN);
if (Lists.isEmpty(tokens)) {
return Lists.empty();
}
final boolean isCurrentUser = userId.equals(SecurityUtils.getLoginUserId());
// 返回
return tokens.stream()
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getStatus()))
.map(LoginTokenDTO::getOrigin)
.map(s -> UserSessionVO.builder()
.current(isCurrentUser && s.getLoginTime().equals(SecurityUtils.getLoginTimestamp()))
.address(s.getAddress())
.location(s.getLocation())
.userAgent(s.getUserAgent())
.loginTime(new Date(s.getLoginTime()))
.build())
.sorted(Comparator.comparing(UserSessionVO::getCurrent).reversed()
.thenComparing(Comparator.comparing(UserSessionVO::getLoginTime).reversed()))
.collect(Collectors.toList());
}
@Override
public void offlineUserSession(UserSessionOfflineRequest request) {
Long userId = Valid.notNull(request.getUserId());
Long timestamp = request.getTimestamp();
// 查询用户
SystemUserDO user = systemUserDAO.selectById(userId);
Valid.notNull(user, ErrorMessage.USER_ABSENT);
// 添加日志参数
OperatorLogs.add(OperatorLogs.USERNAME, user.getUsername());
// 删除刷新缓存
RedisStrings.delete(UserCacheKeyDefine.LOGIN_REFRESH.format(userId, request.getTimestamp()));
// 查询并且覆盖 token
String tokenKey = UserCacheKeyDefine.LOGIN_TOKEN.format(userId, timestamp);
LoginTokenDTO tokenInfo = RedisStrings.getJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN);
if (tokenInfo != null) {
tokenInfo.setStatus(LoginTokenStatusEnum.SESSION_OFFLINE.getStatus());
LoginTokenIdentityDTO override = new LoginTokenIdentityDTO();
override.setLoginTime(System.currentTimeMillis());
// 设置请求信息
Requests.fillIdentity(override);
tokenInfo.setOverride(override);
// 更新 token
RedisStrings.setJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN, tokenInfo);
public boolean isAdminUser(Long userId) {
// 查询用户角色
List<Long> roleIdList = systemUserRoleDAO.selectRoleIdByUserId(userId);
if (!roleIdList.isEmpty()) {
// 查询角色信息
return systemRoleDAO.selectBatchIds(roleIdList)
.stream()
.map(SystemRoleDO::getCode)
.anyMatch(RoleDefine::isAdmin);
}
return false;
}
/**

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.orion.ops.module.infra.dao.DataPermissionDAO">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.orion.ops.module.infra.entity.domain.DataPermissionDO">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="role_id" property="roleId"/>
<result column="rel_id" property="relId"/>
<result column="type" property="type"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="creator" property="creator"/>
<result column="updater" property="updater"/>
<result column="deleted" property="deleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, user_id, role_id, rel_id, type, create_time, update_time, creator, updater, deleted
</sql>
</mapper>