|
@@ -2,12 +2,16 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import cn.hutool.core.util.ArrayUtil;
|
|
|
+import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
|
|
|
+import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
|
|
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO;
|
|
|
+import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer;
|
|
|
import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
|
|
|
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
|
|
|
import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
|
|
@@ -18,23 +22,28 @@ import com.google.common.collect.Multimap;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronization;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
import javax.annotation.Resource;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Set;
|
|
|
+import java.util.*;
|
|
|
|
|
|
|
|
|
* 权限 Service 实现类
|
|
|
*
|
|
|
* @author 芋道源码
|
|
|
*/
|
|
|
-@Service
|
|
|
+@Service("ss")
|
|
|
@Slf4j
|
|
|
public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
|
|
|
+
|
|
|
+ * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
|
|
+ * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
|
|
+ */
|
|
|
+ private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
|
|
+
|
|
|
|
|
|
* 角色编号与菜单编号的缓存映射
|
|
|
* key:角色编号
|
|
@@ -51,6 +60,10 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
|
|
*/
|
|
|
private volatile Multimap<Long, Long> menuRoleCache;
|
|
|
+
|
|
|
+ * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
|
|
|
+ */
|
|
|
+ private volatile Date maxUpdateTime;
|
|
|
|
|
|
@Resource
|
|
|
private SysRoleMenuMapper roleMenuMapper;
|
|
@@ -62,14 +75,22 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
@Resource
|
|
|
private SysMenuService menuService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private SysPermissionProducer permissionProducer;
|
|
|
+
|
|
|
|
|
|
* 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
|
|
|
*/
|
|
|
@Override
|
|
|
@PostConstruct
|
|
|
- public void init() {
|
|
|
+ public void initLocalCache() {
|
|
|
+
|
|
|
+ List<SysRoleMenuDO> roleMenuList = this.loadRoleMenuIfUpdate(maxUpdateTime);
|
|
|
+ if (CollUtil.isEmpty(roleMenuList)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
- List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null);
|
|
|
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
|
|
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
|
|
|
roleMenuList.forEach(roleMenuDO -> {
|
|
@@ -78,9 +99,32 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
});
|
|
|
roleMenuCache = roleMenuCacheBuilder.build();
|
|
|
menuRoleCache = menuRoleCacheBuilder.build();
|
|
|
+ assert roleMenuList.size() > 0;
|
|
|
+ maxUpdateTime = roleMenuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
|
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * 如果角色与菜单的关联发生变化,从数据库中获取最新的全量角色与菜单的关联。
|
|
|
+ * 如果未发生变化,则返回空
|
|
|
+ *
|
|
|
+ * @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
|
|
|
+ * @return 角色与菜单的关联列表
|
|
|
+ */
|
|
|
+ private List<SysRoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
|
|
+
|
|
|
+ if (maxUpdateTime == null) {
|
|
|
+ log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]");
|
|
|
+ } else {
|
|
|
+ if (!roleMenuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ log.info("[loadRoleMenuIfUpdate][增量加载全量角色与菜单的关联]");
|
|
|
+ }
|
|
|
+
|
|
|
+ return roleMenuMapper.selectList();
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
|
|
Collection<Integer> menusStatuses) {
|
|
@@ -140,6 +184,15 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
|
|
|
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
|
|
}
|
|
|
+
|
|
|
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void afterCommit() {
|
|
|
+ permissionProducer.sendRoleMenuRefreshMessage();
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -189,4 +242,39 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public boolean hasPermission(String permission) {
|
|
|
+ return hasAnyPermissions(permission);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean hasAnyPermissions(String... permissions) {
|
|
|
+
|
|
|
+ if (ArrayUtil.isEmpty(permissions)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ Set<Long> roleIds = SecurityUtils.getLoginUserRoleIds();
|
|
|
+ if (CollUtil.isEmpty(roleIds)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (roleService.hasAnyAdmin(roleIds)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return Arrays.stream(permissions).anyMatch(permission -> {
|
|
|
+ List<SysMenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(menuList)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds,
|
|
|
+ menuRoleCache.get(menu.getId())));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
}
|