refactor: 代码重构优化,用户权限缓存调整角色权限缓存

This commit is contained in:
haoxr
2023-11-29 22:17:16 +08:00
parent b2374bda69
commit c4463cfcc1
31 changed files with 665 additions and 239 deletions

View File

@@ -2,18 +2,33 @@ package com.youlai.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.system.model.bo.RolePermsBO;
import com.youlai.system.model.entity.SysRoleMenu;
import java.util.List;
import java.util.Set;
/**
* 角色菜单业务接口
*
* @author haoxr
* @since 2.5.0
*/
public interface SysRoleMenuService extends IService<SysRoleMenu> {
/**
* 获取角色拥有的菜单ID集合
*
* @param roleId
* @return
* @param roleId 角色ID
* @return 菜单ID集合
*/
List<Long> listMenuIdsByRoleId(Long roleId);
/**
* 获取角色和权限的列表
*
* @return 角色权限的列表
*/
List<RolePermsBO> getRolePermsList(String roleCode);
}

View File

@@ -1,17 +1,21 @@
package com.youlai.system.service.impl;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.ICaptcha;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.core.security.jwt.JwtTokenProvider;
import com.youlai.system.model.dto.CaptchaResult;
import com.youlai.system.model.dto.LoginResult;
import com.youlai.system.plugin.captcha.CaptchaProperties;
import com.youlai.system.service.AuthService;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -40,6 +44,8 @@ public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager;
private final StringRedisTemplate redisTemplate;
private final JwtTokenProvider jwtTokenProvider;
private final AbstractCaptcha abstractCaptcha;
private final CaptchaProperties captchaProperties;
/**
* 登录
@@ -73,9 +79,9 @@ public class AuthServiceImpl implements AuthService {
Date expiration = claims.getExpiration();
if (expiration != null) {
long ttl = expiration.getTime() - System.currentTimeMillis();
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
} else {
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
}
}
SecurityContextHolder.clearContext();
@@ -88,18 +94,13 @@ public class AuthServiceImpl implements AuthService {
*/
@Override
public CaptchaResult getCaptcha() {
MathGenerator mathGenerator=new MathGenerator(1);
CircleCaptcha circleCaptcha =new CircleCaptcha(120,25,4,3);
circleCaptcha.setGenerator(mathGenerator);
circleCaptcha.setFont(new Font(SANS_SERIF, Font.BOLD, 18));
String captchaCode = circleCaptcha.getCode(); // 验证码
String captchaBase64 = circleCaptcha.getImageBase64Data(); // 验证码图片Base64
String captchaCode = abstractCaptcha.getCode(); // 验证码
String captchaBase64 = abstractCaptcha.getImageBase64Data(); // 验证码图片Base64
// 验证码文本缓存至Redis用于登录校验
String captchaKey = IdUtil.fastSimpleUUID();
redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_CACHE_PREFIX + captchaKey, captchaCode,
120, TimeUnit.SECONDS);
redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode,
captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);
return CaptchaResult.builder()
.captchaKey(captchaKey)

View File

@@ -1,6 +1,7 @@
package com.youlai.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -17,7 +18,7 @@ import com.youlai.system.service.SysDeptService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -36,7 +37,7 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
private final DeptConverter deptConverter;
/**
* 部门列表
* 获取部门列表
*/
@Override
public List<DeptVO> listDepartments(DeptQuery queryParams) {
@@ -52,29 +53,33 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
.orderByAsc(SysDept::getSort)
);
if (CollectionUtil.isEmpty(deptList)) {
return Collections.EMPTY_LIST;
}
// 获取所有部门ID
Set<Long> deptIds = deptList.stream()
.map(SysDept::getId)
.collect(Collectors.toSet());
// 获取父节点ID
Set<Long> parentIds = deptList.stream()
.map(SysDept::getParentId)
.collect(Collectors.toSet());
// 获取根节点ID递归的起点即父节点ID中不包含在部门ID中的节点注意这里不能拿顶级部门 O 作为根节点,因为部门筛选的时候 O 会被过滤掉
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
List<DeptVO> list = new ArrayList<>();
for (Long rootId : rootIds) {
list.addAll(recurDeptList(rootId, deptList));
}
return list;
// 递归生成部门树形列表
return rootIds.stream()
.flatMap(rootId -> recurDeptList(rootId, deptList).stream())
.toList();
}
/**
* 递归生成部门树形列表
*
* @param parentId
* @param deptList
* @return
* @param parentId 父ID
* @param deptList 部门列表
* @return 部门树形列表
*/
public List<DeptVO> recurDeptList(Long parentId, List<SysDept> deptList) {
return deptList.stream()
@@ -100,54 +105,93 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
.select(SysDept::getId, SysDept::getParentId, SysDept::getName)
.orderByAsc(SysDept::getSort)
);
Set<Long> parentIds = deptList.stream()
.map(SysDept::getParentId)
.collect(Collectors.toSet());
if (CollectionUtil.isEmpty(deptList)) {
return Collections.EMPTY_LIST;
}
Set<Long> deptIds = deptList.stream()
.map(SysDept::getId)
.collect(Collectors.toSet());
Set<Long> parentIds = deptList.stream()
.map(SysDept::getParentId)
.collect(Collectors.toSet());
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
List<Option> list = new ArrayList<>();
for (Long rootId : rootIds) {
list.addAll(recurDeptTreeOptions(rootId, deptList));
}
return list;
// 递归生成部门树形列表
return rootIds.stream()
.flatMap(rootId -> recurDeptTreeOptions(rootId, deptList).stream())
.toList();
}
/**
* 新增部门
*
* @param formData 部门表单
* @return 部门ID
*/
@Override
public Long saveDept(DeptForm formData) {
// 校验部门名称是否存在
String name = formData.getName();
long count = this.count(new LambdaQueryWrapper<SysDept>()
.eq(SysDept::getName, name)
);
Assert.isTrue(count == 0, "部门名称已存在");
// form->entity
SysDept entity = deptConverter.form2Entity(formData);
// 部门路径
// 生成部门路径(tree_path)格式父节点tree_path + , + 父节点ID用于删除部门时级联删除子部门
String treePath = generateDeptTreePath(formData.getParentId());
entity.setTreePath(treePath);
// 保存部门并返回部门ID
this.save(entity);
boolean result = this.save(entity);
Assert.isTrue(result, "部门保存失败");
return entity.getId();
}
/**
* 更新部门
*
* @param deptId 部门ID
* @param formData 部门表单
* @return 部门ID
*/
@Override
public Long updateDept(Long deptId, DeptForm formData) {
// 校验部门名称是否存在
String name = formData.getName();
long count = this.count(new LambdaQueryWrapper<SysDept>()
.eq(SysDept::getName, name)
.ne(SysDept::getId, deptId)
);
Assert.isTrue(count == 0, "部门名称已存在");
// form->entity
SysDept entity = deptConverter.form2Entity(formData);
entity.setId(deptId);
// 部门路径
// 生成部门路径(tree_path)格式父节点tree_path + , + 父节点ID用于删除部门时级联删除子部门
String treePath = generateDeptTreePath(formData.getParentId());
entity.setTreePath(treePath);
// 保存部门并返回部门ID
this.updateById(entity);
boolean result = this.updateById(entity);
Assert.isTrue(result, "部门更新失败");
return entity.getId();
}
/**
* 递归生成部门表格层级列表
*
* @param parentId
* @param deptList
* @return
* @param parentId 父ID
* @param deptList 部门列表
* @return 部门表格层级列表
*/
public static List<Option> recurDeptTreeOptions(long parentId, List<SysDept> deptList) {
List<Option> list = CollectionUtil.emptyIfNull(deptList).stream()
@@ -169,7 +213,7 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
* 删除部门
*
* @param ids 部门ID多个以英文逗号,拼接字符串
* @return
* @return 是否删除成功
*/
@Override
public boolean deleteByIds(String ids) {
@@ -189,8 +233,8 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
/**
* 获取部门详情
*
* @param deptId
* @return
* @param deptId 部门ID
* @return 部门表单对象
*/
@Override
public DeptForm getDeptForm(Long deptId) {

View File

@@ -1,7 +1,6 @@
package com.youlai.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -10,6 +9,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.enums.MenuTypeEnum;
import com.youlai.system.common.enums.StatusEnum;
import com.youlai.system.common.model.Option;
import com.youlai.system.converter.MenuConverter;
import com.youlai.system.mapper.SysMenuMapper;
import com.youlai.system.model.bo.RouteBO;
@@ -17,7 +17,6 @@ import com.youlai.system.model.entity.SysMenu;
import com.youlai.system.model.form.MenuForm;
import com.youlai.system.model.query.MenuQuery;
import com.youlai.system.model.vo.MenuVO;
import com.youlai.system.common.model.Option;
import com.youlai.system.model.vo.RouteVO;
import com.youlai.system.service.SysMenuService;
import lombok.RequiredArgsConstructor;
@@ -30,8 +29,6 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path;
/**
* 菜单业务实现类
*
@@ -41,8 +38,10 @@ import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path;
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
private final MenuConverter menuConverter;
/**
* 菜单列表
*
@@ -54,25 +53,25 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
.like(StrUtil.isNotBlank(queryParams.getKeywords()), SysMenu::getName, queryParams.getKeywords())
.orderByAsc(SysMenu::getSort)
);
Set<Long> parentIds = menus.stream()
.map(SysMenu::getParentId)
.collect(Collectors.toSet());
// 获取所有菜单ID
Set<Long> menuIds = menus.stream()
.map(SysMenu::getId)
.collect(Collectors.toSet());
// 获取根节点ID
// 获取所有父级ID
Set<Long> parentIds = menus.stream()
.map(SysMenu::getParentId)
.collect(Collectors.toSet());
// 获取根节点ID递归的起点即父节点ID中不包含在部门ID中的节点注意这里不能拿顶级菜单 O 作为根节点,因为菜单筛选的时候 O 会被过滤掉
List<Long> rootIds = parentIds.stream()
.filter(id -> !menuIds.contains(id))
.toList();
// 使用递归函数来构建菜单树
List<MenuVO> menuList = rootIds.stream()
return rootIds.stream()
.flatMap(rootId -> buildMenuTree(rootId, menus).stream())
.collect(Collectors.toList());
return menuList;
}
/**
@@ -99,7 +98,8 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
*/
@Override
public List<Option> listMenuOptions() {
List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>().orderByAsc(SysMenu::getSort));
List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>()
.orderByAsc(SysMenu::getSort));
return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
}
@@ -251,35 +251,33 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
}
/**
* 获取角色权限集合
* 获取角色权限(Code)集合
*
* @param roles
* @return
* @param roles 角色Code集合
* @return 权限集合
*/
@Override
public Set<String> listRolePerms(Set<String> roles) {
Set<String> perms = this.baseMapper.listRolePerms(roles);
return perms;
return this.baseMapper.listRolePerms(roles);
}
/**
* 获取菜单表单数据
*
* @param id 菜单ID
* @return
* @return 菜单表单数据
*/
@Override
public MenuForm getMenuForm(Long id) {
SysMenu entity = this.getById(id);
MenuForm menuForm = menuConverter.entity2Form(entity);
return menuForm;
return menuConverter.entity2Form(entity);
}
/**
* 删除菜单
*
* @param id 菜单ID
* @return
* @return 是否删除成功
*/
@Override
public boolean deleteMenu(Long id) {
@@ -289,6 +287,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
.or()
.apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", id));
}
// 无异常即为删除成功
return true;
}

View File

@@ -2,20 +2,28 @@ package com.youlai.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.mapper.SysRoleMenuMapper;
import com.youlai.system.model.bo.RolePermsBO;
import com.youlai.system.model.entity.SysRoleMenu;
import com.youlai.system.service.SysRoleMenuService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 角色菜单业务实现
*
* @author haoxr
* @since 2.5.0
*/
@Service
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
/**
* 获取角色拥有的菜单ID集合
*
* @param roleId
* @return
* @param roleId 角色ID
* @return 菜单ID集合
*/
@Override
public List<Long> listMenuIdsByRoleId(Long roleId) {
@@ -23,4 +31,15 @@ public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRo
return menuIds;
}
/**
* 获取权限角色列表
*
* @return 权限角色列表
*/
@Override
public List<RolePermsBO> getRolePermsList(String roleCode) {
List<RolePermsBO> rolePerms= this.baseMapper.getRolePermsList(roleCode);
return rolePerms;
}
}

View File

@@ -46,8 +46,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
/**
* 角色分页列表
*
* @param queryParams
* @return
* @param queryParams 角色查询参数
* @return {@link Page<RolePageVO>} 角色分页列表
*/
@Override
public Page<RolePageVO> getRolePage(RolePageQuery queryParams) {
@@ -69,14 +69,13 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
);
// 实体转换
Page<RolePageVO> pageResult = roleConverter.entity2Page(rolePage);
return pageResult;
return roleConverter.entity2Page(rolePage);
}
/**
* 角色下拉列表
*
* @return
* @return {@link List<Option>} 角色下拉列表
*/
@Override
public List<Option> listRoleOptions() {
@@ -88,15 +87,14 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
);
// 实体转换
List<Option> list = roleConverter.entities2Options(roleList);
return list;
return roleConverter.entities2Options(roleList);
}
/**
* 保存角色
*
* @param roleForm
* @return
* @param roleForm 角色表单数据
* @return {@link Boolean}
*/
@Override
public boolean saveRole(RoleForm roleForm) {
@@ -114,8 +112,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
// 实体转换
SysRole role = roleConverter.form2Entity(roleForm);
boolean result = this.saveOrUpdate(role);
return result;
return this.saveOrUpdate(role);
}
/**
@@ -127,8 +124,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
@Override
public RoleForm getRoleForm(Long roleId) {
SysRole entity = this.getById(roleId);
RoleForm roleForm = roleConverter.entity2Form(entity);
return roleForm;
return roleConverter.entity2Form(entity);
}
/**
@@ -150,22 +146,22 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
* 批量删除角色
*
* @param ids 角色ID多个使用英文逗号(,)分割
* @return
* @return {@link Boolean}
*/
@Override
public boolean deleteRoles(String ids) {
List<Long> roleIds = Arrays.asList(ids.split(",")).stream().map(id -> Long.parseLong(id)).collect(Collectors.toList());
Optional.ofNullable(roleIds)
.orElse(new ArrayList<>())
.forEach(id -> {
long count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, id));
Assert.isTrue(count <= 0, "该角色已分配用户,无法删除");
sysRoleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id));
});
Assert.isTrue(StrUtil.isNotBlank(ids), "删除的角色ID不能为空");
List<Long> roleIds = Arrays.stream(ids.split(","))
.map(Long::parseLong)
.collect(Collectors.toList());
roleIds.forEach(id -> {
long count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, id));
Assert.isTrue(count <= 0, "该角色已分配用户,无法删除");
sysRoleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id));
});
boolean result = this.removeByIds(roleIds);
return result;
return this.removeByIds(roleIds);
}
/**
@@ -176,16 +172,15 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
*/
@Override
public List<Long> getRoleMenuIds(Long roleId) {
List<Long> menuIds = sysRoleMenuService.listMenuIdsByRoleId(roleId);
return menuIds;
return sysRoleMenuService.listMenuIdsByRoleId(roleId);
}
/**
* 修改角色的资源权限
*
* @param roleId
* @param menuIds
* @return
* @param roleId 角色ID
* @param menuIds 菜单ID集合
* @return {@link Boolean}
*/
@Override
@Transactional
@@ -206,8 +201,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
/**
* 获取最大范围的数据权限
*
* @param roles
* @return
* @param roles 角色编码集合
* @return {@link Integer} 数据权限范围
*/
@Override
public Integer getMaximumDataScope(Set<String> roles) {

View File

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.system.common.constant.CacheConstants;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.common.util.DateUtils;
@@ -23,10 +24,7 @@ import com.youlai.system.model.query.UserPageQuery;
import com.youlai.system.model.vo.UserExportVO;
import com.youlai.system.model.vo.UserInfoVO;
import com.youlai.system.model.vo.UserPageVO;
import com.youlai.system.service.SysMenuService;
import com.youlai.system.service.SysRoleService;
import com.youlai.system.service.SysUserRoleService;
import com.youlai.system.service.SysUserService;
import com.youlai.system.service.*;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -34,6 +32,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -60,6 +59,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
private final RedisTemplate redisTemplate;
private final SysRoleMenuService roleMenuService;
/**
* 获取用户分页列表
*
@@ -232,7 +233,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
/**
* 获取登录用户信息
*
* @return
* @return {@link UserInfoVO} 用户信息
*/
@Override
public UserInfoVO getCurrentUserInfo() {
@@ -257,9 +258,16 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
userInfoVO.setRoles(roles);
// 用户权限集合
Set<String> perms = (Set<String>) redisTemplate.opsForValue().get(SecurityConstants.USER_PERMS_CACHE_PREFIX + user.getId());
Set<String> perms = new HashSet<>();
if (CollectionUtil.isNotEmpty(roles)) {
for (String role : roles) {
Set<String> rolePerms = (Set<String>) redisTemplate.opsForHash().get(CacheConstants.ROLE_PERMS_PREFIX, role);
if (CollectionUtil.isNotEmpty(rolePerms)) {
perms.addAll(rolePerms);
}
}
}
userInfoVO.setPerms(perms);
return userInfoVO;
}