增加 get-info 接口的相关方法

This commit is contained in:
YunaiV
2021-01-03 23:28:41 +08:00
parent 0a6078610b
commit 162bebf5fb
32 changed files with 1153 additions and 39 deletions

View File

@@ -1,6 +1,9 @@
package cn.iocoder.dashboard.modules.system.service.auth;
import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
import java.util.Set;
/**
* 认证 Service 接口
@@ -13,4 +16,16 @@ public interface SysAuthService extends SecurityFrameworkService {
String login(String username, String password, String captchaUUID, String captchaCode);
/**
* 获得用户的基本信息
*
* 这里传输 roleIds 参数的原因,是该参数是从 LoginUser 缓存中获取到的,而我们校验权限时也是从 LoginUser 缓存中获取 roleIds
* 通过这样的方式,保持一致
*
* @param userId 用户编号
* @param roleIds 用户拥有的角色编号数组
* @return 用户的信息,包括角色权限和菜单权限
*/
SysAuthGetInfoRespVO getInfo(Long userId, Set<Long> roleIds);
}

View File

@@ -4,13 +4,19 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
import cn.iocoder.dashboard.framework.security.core.LoginUser;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO;
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.enums.user.UserStatus;
import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
import cn.iocoder.dashboard.util.date.DateUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
@@ -27,9 +33,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.*;
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -52,6 +56,11 @@ public class SysAuthServiceImpl implements SysAuthService {
private AuthenticationManager authenticationManager;
@Resource
private SysUserService userService;
@Resource
private SysRoleService roleService;
@Resource
private SysPermissionService permissionService;
@Resource
private SysLoginUserRedisDAO loginUserRedisDAO;
@@ -62,7 +71,6 @@ public class SysAuthServiceImpl implements SysAuthService {
if (user == null) {
throw new UsernameNotFoundException(username);
}
// 创建 LoginUser 对象
return SysAuthConvert.INSTANCE.convert(user);
}
@@ -74,11 +82,10 @@ public class SysAuthServiceImpl implements SysAuthService {
if (user == null) {
throw new UsernameNotFoundException(String.valueOf(userId));
}
// 创建 LoginUser 对象
LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
loginUser.setUpdateTime(new Date());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
return loginUser;
}
@@ -89,11 +96,10 @@ public class SysAuthServiceImpl implements SysAuthService {
// 使用账号密码,进行登陆。
LoginUser loginUser = this.login0(username, password);
// 缓存登陆用户到 Redis 中
String sessionId = IdUtil.fastSimpleUUID();
loginUser.setUpdateTime(new Date());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
loginUserRedisDAO.set(sessionId, loginUser);
// 创建 Token
@@ -143,11 +149,15 @@ public class SysAuthServiceImpl implements SysAuthService {
* 获得 User 拥有的角色编号数组
*
* @param userId 用户编号
* @param deptId 科室编号
* @return 角色编号数组
*/
private Set<Integer> getUserRoleIds(Long userId) {
// TODO 芋艿:读取角色编号
return Collections.emptySet();
private Set<Long> getUserRoleIds(Long userId, Long deptId) {
// 用户拥有的角色
Set<Long> roleIds = new HashSet<>(permissionService.listUserRoleIds(userId));
// 部门拥有的角色
CollectionUtils.addIfNotNull(roleIds, permissionService.getDeptRoleId(deptId));
return roleIds;
}
@Override
@@ -162,7 +172,6 @@ public class SysAuthServiceImpl implements SysAuthService {
// 获得 LoginUser
LoginUser loginUser = loginUserRedisDAO.get(sessionId);
if (loginUser == null) {
// throw exception(AUTH_SESSION_TIMEOUT);
return null;
}
// 刷新 LoginUser 缓存
@@ -176,18 +185,15 @@ public class SysAuthServiceImpl implements SysAuthService {
claims = tokenService.parseToken(token);
} catch (JwtException jwtException) {
log.warn("[verifyToken][token({}) 解析发生异常]", token);
// throw exception(TOKEN_PARSE_FAIL);
return null;
}
// token 已经过期
if (DateUtils.isExpired(claims.getExpiration())) {
// throw exception(TOKEN_EXPIRED);
return null;
}
// 判断 sessionId 是否存在
String sessionId = claims.getSubject();
if (StrUtil.isBlank(sessionId)) {
// throw exception(AUTH_SESSION_ID_NOT_FOUND);
return null;
}
return sessionId;
@@ -207,9 +213,25 @@ public class SysAuthServiceImpl implements SysAuthService {
}
// 刷新 LoginUser 缓存
loginUser.setDeptId(user.getDeptId());
loginUser.setUpdateTime(new Date());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId()));
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId(), loginUser.getDeptId()));
loginUserRedisDAO.set(sessionId, loginUser);
}
@Override
public SysAuthGetInfoRespVO getInfo(Long userId, Set<Long> roleIds) {
// 获得用户信息
SysUserDO user = userService.getUser(userId);
if (user == null) {
return null;
}
// 获得角色列表
List<SysRoleDO> roleList = roleService.listRolesFromCache(roleIds);
// 获得菜单列表
List<SysMenuDO> menuList = permissionService.listRoleMenusFromCache(roleIds);
// 拼接结果返回
return SysAuthConvert.INSTANCE.convert(user, roleList, menuList);
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
import java.util.Collection;
import java.util.List;
/**
* 菜单 Service 接口
*/
public interface SysMenuService {
/**
*
*/
void init();
/**
* 获得所有菜单,从缓存中
*
* @return 菜单列表
*/
List<SysMenuDO> listMenusFromCache();
/**
* 获得指定编号的菜单数组,从缓存中
*
* @param menuIds 菜单编号数组
* @return 菜单数组
*/
List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds);
}

View File

@@ -0,0 +1,46 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
import java.util.Collection;
import java.util.List;
/**
* 权限 Service 接口
*
* 提供用户-角色、角色-菜单、角色-部门的关联权限处理
*
* @author 芋道源码
*/
public interface SysPermissionService {
/**
* 初始化
*/
void init();
/**
* 获得角色们拥有的菜单列表,从缓存中获取
*
* @param roleIds 角色编号素组
* @return 菜单列表
*/
List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds);
/**
* 获得用户拥有的角色编号数组
*
* @param userId 用户编号
* @return 角色编号数组
*/
List<Long> listUserRoleIds(Long userId);
/**
* 获得部门拥有的角色编号
*
* @param deptId 部门编号
* @return 角色编号
*/
Long getDeptRoleId(Long deptId);
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
import java.util.Collection;
import java.util.List;
/**
* 角色 Service 接口
*
* @author 芋道源码
*/
public interface SysRoleService {
/**
* 初始化
*/
void init();
/**
* 获得角色数组,从缓存中
*
* @param roleIds 角色编号数组
* @return 角色数组
*/
List<SysRoleDO> listRolesFromCache(Collection<Long> roleIds);
/**
* 判断角色数组中,是否有管理员
*
* @param roleList 角色数组
* @return 是否有管理员
*/
boolean hasAnyAdmin(Collection<SysRoleDO> roleList);
}

View File

@@ -0,0 +1,81 @@
package cn.iocoder.dashboard.modules.system.service.permission.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 菜单 Service 实现
*/
@Service
@Slf4j
public class SysMenuServiceImpl implements SysMenuService {
/**
* 菜单缓存
* key菜单编号
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Map<Long, SysMenuDO> menuCache;
/**
* 权限与菜单缓存
* key权限 {@link SysMenuDO#getPerms()}
* valueSysMenuDO 数组,因为一个权限可能对应多个 SysMenuDO 对象
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Multimap<String, SysMenuDO> permMenuCache;
@Resource
private SysMenuMapper menuMapper;
/**
* 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存
*/
@Override
@PostConstruct
public void init() {
List<SysMenuDO> menuList = menuMapper.selectList(null);
ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
menuList.forEach(menuDO -> {
menuCacheBuilder.put(menuDO.getMenuId(), menuDO);
permMenuCacheBuilder.put(menuDO.getPerms(), menuDO);
});
menuCache = menuCacheBuilder.build();
permMenuCache = permMenuCacheBuilder.build();
log.info("[init][初始化菜单数量为 {}]", menuList.size());
}
@Override
public List<SysMenuDO> listMenusFromCache() {
// Guava ImmutableMap 对应的 value 类型为 ImmutableCollection
// 而 ImmutableList 在 copyof 时,如果入参类型为 ImmutableCollection 时,会进行包装,而不会进行复制。
return ImmutableList.copyOf(menuCache.values());
}
@Override
public List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds) {
if (CollectionUtil.isEmpty(menuIds)) {
return Collections.emptyList();
}
return menuCache.values().stream().filter(menuDO -> menuIds.contains(menuDO.getMenuId()))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,104 @@
package cn.iocoder.dashboard.modules.system.service.permission.impl;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleDeptMapper;
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.*;
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;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
import cn.iocoder.dashboard.util.collection.MapUtils;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
/**
* 权限 Service 实现类
*
* @author 初始化
*/
@Service
@Slf4j
public class SysPermissionServiceImpl implements SysPermissionService {
/**
* 角色编号与菜单编号的缓存映射
* key角色编号
* value菜单编号的数组
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Multimap<Long, Long> roleMenuCache;
/**
* 菜单编号与角色编号的缓存映射
* key菜单编号
* value角色编号的数组
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Multimap<Long, Long> menuRoleCache;
@Resource
private SysRoleMenuMapper roleMenuMapper;
@Resource
private SysUserRoleMapper userRoleMapper;
@Resource
private SysRoleDeptMapper roleDeptMapper;
@Resource
private SysRoleService roleService;
@Resource
private SysMenuService menuService;
/**
* 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
*/
@Override
@PostConstruct
public void init() {
// 初始化 roleMenuCache 和 menuRoleCache 缓存
List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null);
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
roleMenuList.forEach(roleMenuDO -> {
roleMenuCacheBuilder.put(roleMenuDO.getRoleId(), roleMenuDO.getMenuId());
menuRoleCacheBuilder.put(roleMenuDO.getMenuId(), roleMenuDO.getRoleId());
});
roleMenuCache = roleMenuCacheBuilder.build();
menuRoleCache = menuRoleCacheBuilder.build();
log.info("[init][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
}
@Override
public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds) {
// 判断角色是否包含管理员
List<SysRoleDO> roleList = roleService.listRolesFromCache(roleIds);
boolean hasAdmin = roleService.hasAnyAdmin(roleList);
// 获得角色拥有的菜单关联
if (hasAdmin) { // 管理员,获取到全部
return menuService.listMenusFromCache();
}
List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
return menuService.listMenusFromCache(menuIds);
}
@Override
public List<Long> listUserRoleIds(Long userId) {
List<SysUserRoleDO> userRoleList = userRoleMapper.selectListByUserId(userId);
return CollectionUtils.convertList(userRoleList, SysUserRoleDO::getRoleId);
}
@Override
public Long getDeptRoleId(Long deptId) {
SysRoleDeptDO roleDept = roleDeptMapper.selectById(deptId);
return roleDept != null ? roleDept.getRoleId() : null;
}
}

View File

@@ -0,0 +1,70 @@
package cn.iocoder.dashboard.modules.system.service.permission.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
import cn.iocoder.dashboard.modules.system.enums.permission.RoleKeyEnum;
import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 角色 Service 实现类
*/
@Service
@Slf4j
public class SysRoleServiceImpl implements SysRoleService {
@Resource
private SysRoleMapper roleMapper;
/**
* 角色缓存
* key角色编号 {@link SysRoleDO#getRoleId()}
*
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
*/
private volatile Map<Long, SysRoleDO> roleCache;
/**
* 初始化 {@link #roleCache} 缓存
*/
@Override
@PostConstruct
public void init() {
// 从数据库中读取
List<SysRoleDO> roleDOList = roleMapper.selectList(null);
// 写入缓存
ImmutableMap.Builder<Long, SysRoleDO> builder = ImmutableMap.builder();
roleDOList.forEach(sysRoleDO -> builder.put(sysRoleDO.getRoleId(), sysRoleDO));
roleCache = builder.build();
log.info("[init][初始化 Role 数量为 {}]", roleDOList.size());
}
@Override
public List<SysRoleDO> listRolesFromCache(Collection<Long> roleIds) {
if (CollectionUtil.isEmpty(roleIds)) {
return Collections.emptyList();
}
return roleCache.values().stream().filter(roleDO -> roleIds.contains(roleDO.getRoleId()))
.collect(Collectors.toList());
}
@Override
public boolean hasAnyAdmin(Collection<SysRoleDO> roleList) {
if (CollectionUtil.isEmpty(roleList)) {
return false;
}
return roleList.stream().anyMatch(roleDO -> RoleKeyEnum.ADMIN.getKey().equals(roleDO.getRoleKey()));
}
}