缓存改造:UserRole 使用 Redis 作为缓存
This commit is contained in:
parent
b371225292
commit
9a1a80e4b8
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.system.dal.mysql.permission;
|
package cn.iocoder.yudao.module.system.dal.mysql.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
@ -9,7 +10,7 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
// TODO 芋艿:@TenantDS
|
@TenantDS
|
||||||
public interface UserRoleMapper extends BaseMapperX<UserRoleDO> {
|
public interface UserRoleMapper extends BaseMapperX<UserRoleDO> {
|
||||||
|
|
||||||
default List<UserRoleDO> selectListByUserId(Long userId) {
|
default List<UserRoleDO> selectListByUserId(Long userId) {
|
||||||
|
|
|
@ -30,8 +30,16 @@ public interface RedisKeyConstants {
|
||||||
* 角色的缓存
|
* 角色的缓存
|
||||||
*
|
*
|
||||||
* KEY 格式:role::{id}
|
* KEY 格式:role::{id}
|
||||||
* 数据格式:String
|
* 数据类型:String 角色编号
|
||||||
*/
|
*/
|
||||||
String ROLE = "role";
|
String ROLE = "role";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户拥有的角色编号的缓存
|
||||||
|
*
|
||||||
|
* KEY 格式:user_role_ids::{userId}
|
||||||
|
* 数据类型:String 角色编号集合
|
||||||
|
*/
|
||||||
|
String USER_ROLE_ID = "user_role_id";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.system.mq.consumer.permission;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
|
||||||
import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;
|
|
||||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 针对 {@link UserRoleRefreshMessage} 的消费者
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
public class UserRoleRefreshConsumer extends AbstractChannelMessageListener<UserRoleRefreshMessage> {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private PermissionService permissionService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessage(UserRoleRefreshMessage message) {
|
|
||||||
log.info("[onMessage][收到 User 与 Role 的关联刷新消息]");
|
|
||||||
permissionService.initLocalCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.system.mq.message.permission;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户与角色的数据刷新 Message
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class UserRoleRefreshMessage extends AbstractChannelMessage {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getChannel() {
|
|
||||||
return "system.user-role.refresh";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.system.mq.producer.permission;
|
package cn.iocoder.yudao.module.system.mq.producer.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage;
|
|
||||||
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
|
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
|
||||||
import cn.iocoder.yudao.module.system.mq.message.permission.UserRoleRefreshMessage;
|
import cn.iocoder.yudao.module.system.mq.message.permission.RoleMenuRefreshMessage;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -24,12 +23,4 @@ public class PermissionProducer {
|
||||||
redisMQTemplate.send(message);
|
redisMQTemplate.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送 {@link UserRoleRefreshMessage} 消息
|
|
||||||
*/
|
|
||||||
public void sendUserRoleRefreshMessage() {
|
|
||||||
UserRoleRefreshMessage message = new UserRoleRefreshMessage();
|
|
||||||
redisMQTemplate.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
package cn.iocoder.yudao.module.system.service.permission;
|
package cn.iocoder.yudao.module.system.service.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
|
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Collections.singleton;
|
import static java.util.Collections.singleton;
|
||||||
|
@ -25,28 +21,6 @@ public interface PermissionService {
|
||||||
*/
|
*/
|
||||||
void initLocalCache();
|
void initLocalCache();
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得角色们拥有的菜单列表,从缓存中获取
|
|
||||||
*
|
|
||||||
* 任一参数为空时,则返回为空
|
|
||||||
*
|
|
||||||
* @param roleIds 角色编号数组
|
|
||||||
* @param menuTypes 菜单类型数组
|
|
||||||
* @param menusStatuses 菜单状态数组
|
|
||||||
* @return 菜单列表
|
|
||||||
*/
|
|
||||||
List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
|
||||||
Collection<Integer> menusStatuses);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得用户拥有的角色编号集合,从缓存中获取
|
|
||||||
*
|
|
||||||
* @param userId 用户编号
|
|
||||||
* @param roleStatuses 角色状态集合. 允许为空,为空时不过滤
|
|
||||||
* @return 角色编号集合
|
|
||||||
*/
|
|
||||||
Set<Long> getUserRoleIdsFromCache(Long userId, @Nullable Collection<Integer> roleStatuses);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得角色拥有的菜单编号集合
|
* 获得角色拥有的菜单编号集合
|
||||||
*
|
*
|
||||||
|
@ -89,6 +63,14 @@ public interface PermissionService {
|
||||||
*/
|
*/
|
||||||
Set<Long> getUserRoleIdListByUserId(Long userId);
|
Set<Long> getUserRoleIdListByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得用户拥有的角色编号集合,从缓存中获取
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @return 角色编号集合
|
||||||
|
*/
|
||||||
|
Set<Long> getUserRoleIdListByUserIdFromCache(Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置用户角色
|
* 设置用户角色
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.system.service.permission;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
||||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
|
@ -18,6 +17,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
|
||||||
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
|
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
|
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
|
||||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||||
|
@ -30,6 +30,7 @@ import com.google.common.collect.Sets;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
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.transaction.support.TransactionSynchronization;
|
import org.springframework.transaction.support.TransactionSynchronization;
|
||||||
|
@ -41,7 +42,7 @@ import java.util.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static java.util.Collections.singleton;
|
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限 Service 实现类
|
* 权限 Service 实现类
|
||||||
|
@ -52,16 +53,6 @@ import static java.util.Collections.singleton;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PermissionServiceImpl implements PermissionService {
|
public class PermissionServiceImpl implements PermissionService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色编号与菜单编号的缓存映射
|
|
||||||
* key:角色编号
|
|
||||||
* value:菜单编号的数组
|
|
||||||
*
|
|
||||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@Setter // 单元测试需要
|
|
||||||
private volatile Multimap<Long, Long> roleMenuCache;
|
|
||||||
/**
|
/**
|
||||||
* 菜单编号与角色编号的缓存映射
|
* 菜单编号与角色编号的缓存映射
|
||||||
* key:菜单编号
|
* key:菜单编号
|
||||||
|
@ -73,17 +64,6 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
@Setter // 单元测试需要
|
@Setter // 单元测试需要
|
||||||
private volatile Multimap<Long, Long> menuRoleCache;
|
private volatile Multimap<Long, Long> menuRoleCache;
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户编号与角色编号的缓存映射
|
|
||||||
* key:用户编号
|
|
||||||
* value:角色编号的数组
|
|
||||||
*
|
|
||||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@Setter // 单元测试需要
|
|
||||||
private volatile Map<Long, Set<Long>> userRoleCache;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RoleMenuMapper roleMenuMapper;
|
private RoleMenuMapper roleMenuMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -105,7 +85,6 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initLocalCache() {
|
public void initLocalCache() {
|
||||||
initLocalCacheForRoleMenu();
|
initLocalCacheForRoleMenu();
|
||||||
initLocalCacheForUserRole();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,71 +99,12 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
|
log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
|
||||||
|
|
||||||
// 第二步:构建缓存
|
// 第二步:构建缓存
|
||||||
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
|
||||||
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
|
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
|
||||||
roleMenus.forEach(roleMenuDO -> {
|
roleMenus.forEach(roleMenuDO -> menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId()));
|
||||||
roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId());
|
|
||||||
menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId());
|
|
||||||
});
|
|
||||||
roleMenuCache = roleMenuCacheBuilder.build();
|
|
||||||
menuRoleCache = menuRoleCacheBuilder.build();
|
menuRoleCache = menuRoleCacheBuilder.build();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新 UserRole 本地缓存
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void initLocalCacheForUserRole() {
|
|
||||||
// 注意:忽略自动多租户,因为要全局初始化缓存
|
|
||||||
TenantUtils.executeIgnore(() -> {
|
|
||||||
// 第一步:加载数据
|
|
||||||
List<UserRoleDO> userRoles = userRoleMapper.selectList();
|
|
||||||
log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
|
|
||||||
|
|
||||||
// 第二步:构建缓存。
|
|
||||||
ImmutableMultimap.Builder<Long, Long> userRoleCacheBuilder = ImmutableMultimap.builder();
|
|
||||||
userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));
|
|
||||||
userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
|
||||||
Collection<Integer> menusStatuses) {
|
|
||||||
// 任一一个参数为空时,不返回任何菜单
|
|
||||||
if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断角色是否包含超级管理员。如果是超级管理员,获取到全部
|
|
||||||
if (roleService.hasAnySuperAdmin(roleIds)) {
|
|
||||||
return menuService.getMenuListFromCache(menuTypes, menusStatuses);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得角色拥有的菜单关联
|
|
||||||
List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
|
|
||||||
return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Long> getUserRoleIdsFromCache(Long userId, Collection<Integer> roleStatuses) {
|
|
||||||
Set<Long> cacheRoleIds = userRoleCache.get(userId);
|
|
||||||
// 创建用户的时候没有分配角色,会存在空指针异常
|
|
||||||
if (CollUtil.isEmpty(cacheRoleIds)) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
Set<Long> roleIds = new HashSet<>(cacheRoleIds);
|
|
||||||
// 过滤角色状态
|
|
||||||
if (CollectionUtil.isNotEmpty(roleStatuses)) {
|
|
||||||
roleIds.removeIf(roleId -> {
|
|
||||||
RoleDO role = roleService.getRoleFromCache(roleId);
|
|
||||||
return role == null || !roleStatuses.contains(role.getStatus());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return roleIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
|
public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
|
||||||
// 如果是管理员的情况下,获取全部菜单编号
|
// 如果是管理员的情况下,获取全部菜单编号
|
||||||
|
@ -228,14 +148,33 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> getUserRoleIdListByUserId(Long userId) {
|
public Set<Long> getUserRoleIdListByUserId(Long userId) {
|
||||||
return convertSet(userRoleMapper.selectListByUserId(userId),
|
return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);
|
||||||
UserRoleDO::getRoleId);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Cacheable(value = RedisKeyConstants.USER_ROLE_ID, key = "#userId")
|
||||||
|
public Set<Long> getUserRoleIdListByUserIdFromCache(Long userId) {
|
||||||
|
return getUserRoleIdListByUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
|
public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
|
||||||
return convertSet(userRoleMapper.selectListByRoleIds(roleIds),
|
return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId);
|
||||||
UserRoleDO::getUserId);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得用户拥有的角色,并且这些角色是开启状态的
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @return 用户拥有的角色
|
||||||
|
*/
|
||||||
|
private List<RoleDO> getEnableUserRoleListByUserIdFromCache(Long userId) {
|
||||||
|
// 获得用户拥有的角色编号
|
||||||
|
Set<Long> roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId);
|
||||||
|
// 获得角色数组,并移除被禁用的
|
||||||
|
List<RoleDO> roles = roleService.getRoleListFromCache(roleIds);
|
||||||
|
roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
|
||||||
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -259,15 +198,6 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
|
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
|
||||||
userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
|
userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
|
||||||
}
|
}
|
||||||
// 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
permissionProducer.sendUserRoleRefreshMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -282,16 +212,6 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
userRoleMapper.deleteListByRoleId(roleId);
|
userRoleMapper.deleteListByRoleId(roleId);
|
||||||
// 标记删除 RoleMenu
|
// 标记删除 RoleMenu
|
||||||
roleMenuMapper.deleteListByRoleId(roleId);
|
roleMenuMapper.deleteListByRoleId(roleId);
|
||||||
// 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
permissionProducer.sendRoleMenuRefreshMessage();
|
|
||||||
permissionProducer.sendUserRoleRefreshMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -313,14 +233,6 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void processUserDeleted(Long userId) {
|
public void processUserDeleted(Long userId) {
|
||||||
userRoleMapper.deleteListByUserId(userId);
|
userRoleMapper.deleteListByUserId(userId);
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
permissionProducer.sendUserRoleRefreshMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -331,26 +243,41 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得当前登录的角色。如果为空,说明没有权限
|
// 获得当前登录的角色。如果为空,说明没有权限
|
||||||
Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
|
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
|
||||||
if (CollUtil.isEmpty(roleIds)) {
|
if (CollUtil.isEmpty(roles)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 判断是否是超管。如果是,当然符合条件
|
|
||||||
if (roleService.hasAnySuperAdmin(roleIds)) {
|
// 情况一:遍历判断每个权限,如果有一满足,说明有权限
|
||||||
return true;
|
for (String permission : permissions) {
|
||||||
|
if (hasAnyPermission(roles, permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 遍历权限,判断是否有一个满足
|
// 情况二:如果是超管,也说明有权限
|
||||||
return Arrays.stream(permissions).anyMatch(permission -> {
|
return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId));
|
||||||
List<MenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);
|
}
|
||||||
// 采用严格模式,如果权限找不到对应的 Menu 的话,认为
|
|
||||||
if (CollUtil.isEmpty(menuList)) {
|
/**
|
||||||
return false;
|
* 判断指定角色,是否拥有该 permission 权限
|
||||||
}
|
*
|
||||||
// 获得是否拥有该权限,任一一个
|
* @param roles 指定角色数组
|
||||||
return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds,
|
* @param permission 权限标识
|
||||||
menuRoleCache.get(menu.getId())));
|
* @return 是否拥有
|
||||||
});
|
*/
|
||||||
|
// TODO 芋艿:要想想咋继续优化
|
||||||
|
private boolean hasAnyPermission(List<RoleDO> roles, String permission) {
|
||||||
|
List<MenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);
|
||||||
|
// 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限
|
||||||
|
if (CollUtil.isEmpty(menuList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得是否拥有该权限,任一一个
|
||||||
|
Set<Long> roleIds = convertSet(roles, RoleDO::getId);
|
||||||
|
return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds,
|
||||||
|
menuRoleCache.get(menu.getId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -361,32 +288,29 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得当前登录的角色。如果为空,说明没有权限
|
// 获得当前登录的角色。如果为空,说明没有权限
|
||||||
Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
|
List<RoleDO> roleList = getEnableUserRoleListByUserIdFromCache(userId);
|
||||||
if (CollUtil.isEmpty(roleIds)) {
|
if (CollUtil.isEmpty(roleList)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 判断是否是超管。如果是,当然符合条件
|
|
||||||
if (roleService.hasAnySuperAdmin(roleIds)) {
|
// 判断是否有角色
|
||||||
return true;
|
Set<String> userRoles = convertSet(roleList, RoleDO::getCode);
|
||||||
}
|
|
||||||
Set<String> userRoles = convertSet(roleService.getRoleListFromCache(roleIds),
|
|
||||||
RoleDO::getCode);
|
|
||||||
return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
|
return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
|
@DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
|
||||||
@TenantIgnore // 忽略多租户的自动过滤。如果不忽略,会导致添加租户时,因为切换租户,导致获取不到 User。即使忽略,本身该方法不存在跨租户的操作,不会存在问题。
|
@TenantIgnore // 忽略多租户的自动过滤。如果不忽略,会导致添加租户时,因为切换租户,导致获取不到 User。即使忽略,本身该方法不存在跨租户的操作,不会存在问题。 // TODO 芋艿:看看怎么去掉
|
||||||
public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
|
public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
|
||||||
// 获得用户的角色
|
// 获得用户的角色
|
||||||
Set<Long> roleIds = getUserRoleIdsFromCache(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
|
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
|
||||||
|
|
||||||
// 如果角色为空,则只能查看自己
|
// 如果角色为空,则只能查看自己
|
||||||
DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
|
DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
|
||||||
if (CollUtil.isEmpty(roleIds)) {
|
if (CollUtil.isEmpty(roles)) {
|
||||||
result.setSelf(true);
|
result.setSelf(true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
List<RoleDO> roles = roleService.getRoleListFromCache(roleIds);
|
|
||||||
|
|
||||||
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
|
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
|
||||||
Supplier<Long> userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
|
Supplier<Long> userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
|
||||||
|
@ -428,9 +352,18 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 未知情况,error log 即可
|
// 未知情况,error log 即可
|
||||||
log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result));
|
log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得自身的代理对象,解决 AOP 生效问题
|
||||||
|
*
|
||||||
|
* @return 自己
|
||||||
|
*/
|
||||||
|
private PermissionServiceImpl getSelf() {
|
||||||
|
return SpringUtil.getBean(getClass());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,44 +98,6 @@ public class PermissionServiceTest extends BaseDbUnitTest {
|
||||||
assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L));
|
assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetRoleMenuListFromCache_superAdmin() {
|
|
||||||
// 准备参数
|
|
||||||
Collection<Long> roleIds = singletonList(100L);
|
|
||||||
Collection<Integer> menuTypes = asList(2, 3);
|
|
||||||
Collection<Integer> menusStatuses = asList(0, 1);
|
|
||||||
// mock 方法
|
|
||||||
List<RoleDO> roleList = singletonList(randomPojo(RoleDO.class, o -> o.setId(100L)));
|
|
||||||
when(roleService.getRoleListFromCache(eq(roleIds))).thenReturn(roleList);
|
|
||||||
when(roleService.hasAnySuperAdmin(same(roleList))).thenReturn(true);
|
|
||||||
List<MenuDO> menuList = randomPojoList(MenuDO.class);
|
|
||||||
when(menuService.getMenuListFromCache(eq(menuTypes), eq(menusStatuses))).thenReturn(menuList);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);
|
|
||||||
// 断言
|
|
||||||
assertSame(menuList, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetRoleMenuListFromCache_normal() {
|
|
||||||
// 准备参数
|
|
||||||
Collection<Long> roleIds = asSet(100L, 200L);
|
|
||||||
Collection<Integer> menuTypes = asList(2, 3);
|
|
||||||
Collection<Integer> menusStatuses = asList(0, 1);
|
|
||||||
// mock 方法
|
|
||||||
Multimap<Long, Long> roleMenuCache = ImmutableMultimap.<Long, Long>builder().put(100L, 1000L)
|
|
||||||
.put(200L, 2000L).put(200L, 2001L).build();
|
|
||||||
permissionService.setRoleMenuCache(roleMenuCache);
|
|
||||||
List<MenuDO> menuList = randomPojoList(MenuDO.class);
|
|
||||||
when(menuService.getMenuListFromCache(eq(asList(1000L, 2000L, 2001L)), eq(menuTypes), eq(menusStatuses))).thenReturn(menuList);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
List<MenuDO> result = permissionService.getRoleMenuListFromCache(roleIds, menuTypes, menusStatuses);
|
|
||||||
// 断言
|
|
||||||
assertSame(menuList, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetUserRoleIdsFromCache() {
|
public void testGetUserRoleIdsFromCache() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
|
|
Loading…
Reference in New Issue