refactor: 代码重构优化,用户权限缓存调整角色权限缓存
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
package com.youlai.system.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存常量
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
public interface CacheConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码缓存前缀
|
||||||
|
*/
|
||||||
|
String CAPTCHA_CODE_PREFIX = "captcha_code:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色和权限缓存前缀
|
||||||
|
*/
|
||||||
|
String ROLE_PERMS_PREFIX = "role_perms:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单Token缓存前缀
|
||||||
|
*/
|
||||||
|
String BLACKLIST_TOKEN_PREFIX = "blacklist_token:";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.youlai.system.common.constant;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Excel 常量
|
|
||||||
*
|
|
||||||
* @author haoxr
|
|
||||||
* @since 3.0.0
|
|
||||||
*/
|
|
||||||
public interface ExcelConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Excel 模板目录
|
|
||||||
*/
|
|
||||||
String EXCEL_TEMPLATE_DIR="excel-templates";
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.youlai.system.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT Claims声明常量
|
||||||
|
* <p>
|
||||||
|
* JWT Claims 属于 Payload 的一部分,包含了一些实体(通常指的用户)的状态和额外的元数据。
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
public interface JwtClaimConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
String USER_ID = "userId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
String USERNAME = "username";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
String DEPT_ID = "deptId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限
|
||||||
|
*/
|
||||||
|
String DATA_SCOPE = "dataScope";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限(角色Code)集合
|
||||||
|
*/
|
||||||
|
String AUTHORITIES = "authorities";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,28 +13,5 @@ public interface SecurityConstants {
|
|||||||
*/
|
*/
|
||||||
String LOGIN_PATH = "/api/v1/auth/login";
|
String LOGIN_PATH = "/api/v1/auth/login";
|
||||||
|
|
||||||
/**
|
|
||||||
* Token 前缀
|
|
||||||
*/
|
|
||||||
String TOKEN_PREFIX = "Bearer ";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 请求头Token的Key
|
|
||||||
*/
|
|
||||||
String TOKEN_KEY = "Authorization";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证码缓存前缀
|
|
||||||
*/
|
|
||||||
String CAPTCHA_CODE_CACHE_PREFIX = "captcha_code:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户权限集合缓存前缀
|
|
||||||
*/
|
|
||||||
String USER_PERMS_CACHE_PREFIX = "user_perms:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 黑名单Token缓存前缀
|
|
||||||
*/
|
|
||||||
String BLACK_TOKEN_CACHE_PREFIX = "blacklist_token:";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,4 @@ public interface SystemConstants {
|
|||||||
* 超级管理员角色编码
|
* 超级管理员角色编码
|
||||||
*/
|
*/
|
||||||
String ROOT_ROLE_CODE = "ROOT";
|
String ROOT_ROLE_CODE = "ROOT";
|
||||||
|
|
||||||
/**
|
|
||||||
* 超级管理员用户名
|
|
||||||
*/
|
|
||||||
String ROOT_USER_NAME = "root";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,28 +36,28 @@ public class SysMenuController {
|
|||||||
|
|
||||||
private final SysMenuService menuService;
|
private final SysMenuService menuService;
|
||||||
|
|
||||||
@Operation(summary = "菜单列表",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "菜单列表")
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<List<MenuVO>> listMenus( @ParameterObject MenuQuery queryParams) {
|
public Result<List<MenuVO>> listMenus( @ParameterObject MenuQuery queryParams) {
|
||||||
List<MenuVO> menuList = menuService.listMenus(queryParams);
|
List<MenuVO> menuList = menuService.listMenus(queryParams);
|
||||||
return Result.success(menuList);
|
return Result.success(menuList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "菜单下拉列表",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "菜单下拉列表")
|
||||||
@GetMapping("/options")
|
@GetMapping("/options")
|
||||||
public Result listMenuOptions() {
|
public Result listMenuOptions() {
|
||||||
List<Option> menus = menuService.listMenuOptions();
|
List<Option> menus = menuService.listMenuOptions();
|
||||||
return Result.success(menus);
|
return Result.success(menus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "路由列表",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "路由列表")
|
||||||
@GetMapping("/routes")
|
@GetMapping("/routes")
|
||||||
public Result<List<RouteVO>> listRoutes() {
|
public Result<List<RouteVO>> listRoutes() {
|
||||||
List<RouteVO> routeList = menuService.listRoutes();
|
List<RouteVO> routeList = menuService.listRoutes();
|
||||||
return Result.success(routeList);
|
return Result.success(routeList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "菜单表单数据",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "菜单表单数据")
|
||||||
@GetMapping("/{id}/form")
|
@GetMapping("/{id}/form")
|
||||||
public Result<MenuForm> getMenuForm(
|
public Result<MenuForm> getMenuForm(
|
||||||
@Parameter(description = "菜单ID") @PathVariable Long id
|
@Parameter(description = "菜单ID") @PathVariable Long id
|
||||||
@@ -66,7 +66,7 @@ public class SysMenuController {
|
|||||||
return Result.success(menu);
|
return Result.success(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "新增菜单",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "新增菜单")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("@ss.hasPerm('sys:menu:add')")
|
@PreAuthorize("@ss.hasPerm('sys:menu:add')")
|
||||||
@PreventDuplicateSubmit
|
@PreventDuplicateSubmit
|
||||||
@@ -76,7 +76,7 @@ public class SysMenuController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "修改菜单",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "修改菜单")
|
||||||
@PutMapping(value = "/{id}")
|
@PutMapping(value = "/{id}")
|
||||||
@PreAuthorize("@ss.hasPerm('sys:menu:edit')")
|
@PreAuthorize("@ss.hasPerm('sys:menu:edit')")
|
||||||
@CacheEvict(cacheNames = "system", key = "'routes'")
|
@CacheEvict(cacheNames = "system", key = "'routes'")
|
||||||
@@ -87,7 +87,7 @@ public class SysMenuController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "删除菜单",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "删除菜单")
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@PreAuthorize("@ss.hasPerm('sys:menu:delete')")
|
@PreAuthorize("@ss.hasPerm('sys:menu:delete')")
|
||||||
@CacheEvict(cacheNames = "system", key = "'routes'")
|
@CacheEvict(cacheNames = "system", key = "'routes'")
|
||||||
@@ -98,7 +98,7 @@ public class SysMenuController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "修改菜单显示状态",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "修改菜单显示状态")
|
||||||
@PatchMapping("/{menuId}")
|
@PatchMapping("/{menuId}")
|
||||||
public Result updateMenuVisible(
|
public Result updateMenuVisible(
|
||||||
@Parameter(description = "菜单ID") @PathVariable Long menuId,
|
@Parameter(description = "菜单ID") @PathVariable Long menuId,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class SysRoleController {
|
|||||||
|
|
||||||
private final SysRoleService roleService;
|
private final SysRoleService roleService;
|
||||||
|
|
||||||
@Operation(summary = "角色分页列表",security = {@SecurityRequirement(name = "Authorization")} )
|
@Operation(summary = "角色分页列表" )
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public PageResult<RolePageVO> getRolePage(
|
public PageResult<RolePageVO> getRolePage(
|
||||||
@ParameterObject RolePageQuery queryParams
|
@ParameterObject RolePageQuery queryParams
|
||||||
@@ -38,14 +38,14 @@ public class SysRoleController {
|
|||||||
return PageResult.success(result);
|
return PageResult.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "角色下拉列表",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "角色下拉列表")
|
||||||
@GetMapping("/options")
|
@GetMapping("/options")
|
||||||
public Result<List<Option>> listRoleOptions() {
|
public Result<List<Option>> listRoleOptions() {
|
||||||
List<Option> list = roleService.listRoleOptions();
|
List<Option> list = roleService.listRoleOptions();
|
||||||
return Result.success(list);
|
return Result.success(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "新增角色",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "新增角色")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("@ss.hasPerm('sys:role:add')")
|
@PreAuthorize("@ss.hasPerm('sys:role:add')")
|
||||||
@PreventDuplicateSubmit
|
@PreventDuplicateSubmit
|
||||||
@@ -54,7 +54,7 @@ public class SysRoleController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "角色表单数据",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "角色表单数据")
|
||||||
@GetMapping("/{roleId}/form")
|
@GetMapping("/{roleId}/form")
|
||||||
public Result<RoleForm> getRoleForm(
|
public Result<RoleForm> getRoleForm(
|
||||||
@Parameter(description ="角色ID") @PathVariable Long roleId
|
@Parameter(description ="角色ID") @PathVariable Long roleId
|
||||||
@@ -63,7 +63,7 @@ public class SysRoleController {
|
|||||||
return Result.success(roleForm);
|
return Result.success(roleForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "修改角色",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "修改角色")
|
||||||
@PutMapping(value = "/{id}")
|
@PutMapping(value = "/{id}")
|
||||||
@PreAuthorize("@ss.hasPerm('sys:role:edit')")
|
@PreAuthorize("@ss.hasPerm('sys:role:edit')")
|
||||||
public Result updateRole(@Valid @RequestBody RoleForm roleForm) {
|
public Result updateRole(@Valid @RequestBody RoleForm roleForm) {
|
||||||
@@ -71,7 +71,7 @@ public class SysRoleController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "删除角色",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "删除角色")
|
||||||
@DeleteMapping("/{ids}")
|
@DeleteMapping("/{ids}")
|
||||||
@PreAuthorize("@ss.hasPerm('sys:role:delete')")
|
@PreAuthorize("@ss.hasPerm('sys:role:delete')")
|
||||||
public Result deleteRoles(
|
public Result deleteRoles(
|
||||||
@@ -81,7 +81,7 @@ public class SysRoleController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "修改角色状态",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "修改角色状态")
|
||||||
@PutMapping(value = "/{roleId}/status")
|
@PutMapping(value = "/{roleId}/status")
|
||||||
public Result updateRoleStatus(
|
public Result updateRoleStatus(
|
||||||
@Parameter(description ="角色ID") @PathVariable Long roleId,
|
@Parameter(description ="角色ID") @PathVariable Long roleId,
|
||||||
@@ -91,7 +91,7 @@ public class SysRoleController {
|
|||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取角色的菜单ID集合",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "获取角色的菜单ID集合")
|
||||||
@GetMapping("/{roleId}/menuIds")
|
@GetMapping("/{roleId}/menuIds")
|
||||||
public Result<List<Long>> getRoleMenuIds(
|
public Result<List<Long>> getRoleMenuIds(
|
||||||
@Parameter(description ="角色ID") @PathVariable Long roleId
|
@Parameter(description ="角色ID") @PathVariable Long roleId
|
||||||
@@ -100,7 +100,7 @@ public class SysRoleController {
|
|||||||
return Result.success(menuIds);
|
return Result.success(menuIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "分配菜单权限给角色",security = {@SecurityRequirement(name = "Authorization")})
|
@Operation(summary = "分配菜单权限给角色")
|
||||||
@PutMapping("/{roleId}/menus")
|
@PutMapping("/{roleId}/menus")
|
||||||
public Result updateRoleMenus(
|
public Result updateRoleMenus(
|
||||||
@PathVariable Long roleId,
|
@PathVariable Long roleId,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.alibaba.excel.EasyExcel;
|
|||||||
import com.alibaba.excel.ExcelWriter;
|
import com.alibaba.excel.ExcelWriter;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.youlai.system.common.constant.ExcelConstants;
|
|
||||||
import com.youlai.system.common.result.PageResult;
|
import com.youlai.system.common.result.PageResult;
|
||||||
import com.youlai.system.common.result.Result;
|
import com.youlai.system.common.result.Result;
|
||||||
import com.youlai.system.common.util.ExcelUtils;
|
import com.youlai.system.common.util.ExcelUtils;
|
||||||
@@ -139,7 +138,7 @@ public class SysUserController {
|
|||||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
|
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
|
||||||
|
|
||||||
String fileClassPath = ExcelConstants.EXCEL_TEMPLATE_DIR + File.separator + fileName;
|
String fileClassPath = "excel-templates" + File.separator + fileName;
|
||||||
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fileClassPath);
|
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fileClassPath);
|
||||||
|
|
||||||
ServletOutputStream outputStream = response.getOutputStream();
|
ServletOutputStream outputStream = response.getOutputStream();
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public interface MenuConverter {
|
|||||||
|
|
||||||
MenuVO entity2Vo(SysMenu entity);
|
MenuVO entity2Vo(SysMenu entity);
|
||||||
|
|
||||||
|
|
||||||
MenuForm entity2Form(SysMenu entity);
|
MenuForm entity2Form(SysMenu entity);
|
||||||
|
|
||||||
SysMenu form2Entity(MenuForm menuForm);
|
SysMenu form2Entity(MenuForm menuForm);
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
public class MybatisPlusConfig {
|
public class MybatisPlusConfig {
|
||||||
|
|
||||||
|
|
||||||
@Value("${system.config.data-permission-enabled}")
|
|
||||||
private Boolean dataPermissionEnabled;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页插件和数据权限插件
|
* 分页插件和数据权限插件
|
||||||
*/
|
*/
|
||||||
@@ -33,9 +29,7 @@ public class MybatisPlusConfig {
|
|||||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
//数据权限
|
//数据权限
|
||||||
if (dataPermissionEnabled) {
|
interceptor.addInnerInterceptor(new DataPermissionInterceptor(new MyDataPermissionHandler()));
|
||||||
interceptor.addInnerInterceptor(new DataPermissionInterceptor(new MyDataPermissionHandler()));
|
|
||||||
}
|
|
||||||
//分页插件
|
//分页插件
|
||||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import java.time.LocalDateTime;
|
|||||||
* mybatis-plus 字段自动填充
|
* mybatis-plus 字段自动填充
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @link <a href="https://mp.baomidou.com/guide/auto-fill-metainfo.html">官方文档</a>
|
|
||||||
* @since 2022/10/14
|
* @since 2022/10/14
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@@ -19,7 +18,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
|
|||||||
/**
|
/**
|
||||||
* 新增填充创建时间
|
* 新增填充创建时间
|
||||||
*
|
*
|
||||||
* @param metaObject
|
* @param metaObject 元数据
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void insertFill(MetaObject metaObject) {
|
public void insertFill(MetaObject metaObject) {
|
||||||
@@ -30,7 +29,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
|
|||||||
/**
|
/**
|
||||||
* 更新填充更新时间
|
* 更新填充更新时间
|
||||||
*
|
*
|
||||||
* @param metaObject
|
* @param metaObject 元数据
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void updateFill(MetaObject metaObject) {
|
public void updateFill(MetaObject metaObject) {
|
||||||
|
|||||||
@@ -14,16 +14,28 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* JWT token 过滤器
|
||||||
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 2023/9/13
|
* @since 2023/9/13
|
||||||
*/
|
*/
|
||||||
public class JwtTokenFilter extends OncePerRequestFilter {
|
public class JwtTokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT Token 工具类
|
||||||
|
*/
|
||||||
private JwtTokenProvider jwtTokenProvider;
|
private JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
|
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
|
||||||
this.jwtTokenProvider = jwtTokenProvider;
|
this.jwtTokenProvider = jwtTokenProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求中获取 JWT Token,校验 JWT Token 是否合法
|
||||||
|
* <p>
|
||||||
|
* 如果合法则将 Authentication 设置到 Spring Security Context 上下文中
|
||||||
|
* 如果不合法则清空 Spring Security Context 上下文,并直接返回响应
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
String token = jwtTokenProvider.resolveToken(request);
|
String token = jwtTokenProvider.resolveToken(request);
|
||||||
@@ -35,7 +47,7 @@ public class JwtTokenFilter extends OncePerRequestFilter {
|
|||||||
} catch (BusinessException ex) {
|
} catch (BusinessException ex) {
|
||||||
//this is very important, since it guarantees the user is not authenticated at all
|
//this is very important, since it guarantees the user is not authenticated at all
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
ResponseUtils.writeErrMsg(response,(ResultCode)ex.getResultCode());
|
ResponseUtils.writeErrMsg(response, (ResultCode) ex.getResultCode());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.youlai.system.core.security.jwt;
|
package com.youlai.system.core.security.jwt;
|
||||||
|
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.JwtClaimConstants;
|
||||||
import com.youlai.system.core.security.model.SysUserDetails;
|
import com.youlai.system.core.security.model.SysUserDetails;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
@@ -10,10 +10,9 @@ import io.jsonwebtoken.io.Decoders;
|
|||||||
import io.jsonwebtoken.io.DecodingException;
|
import io.jsonwebtoken.io.DecodingException;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@@ -25,27 +24,36 @@ import java.util.*;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT token 管理器
|
* JWT token 工具类
|
||||||
|
* <p>
|
||||||
|
* 用于生成/校验/解析 JWT Token
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 2023/9/13
|
* @since 2023/9/13
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtTokenProvider {
|
public class JwtTokenProvider {
|
||||||
|
|
||||||
@Resource
|
/**
|
||||||
private RedisTemplate redisTemplate;
|
* 签名密钥,用于签名 Access Token
|
||||||
|
*/
|
||||||
@Value("${jwt.secret-key:123456}")
|
@Value("${jwt.secret-key:123456}")
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
|
||||||
@Value("${jwt.expiration:7200}")
|
@Value("${jwt.expiration:7200}")
|
||||||
private int expiration;
|
private int expiration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 编码后的签名密钥,用于校验/解析 Access Token
|
||||||
|
*/
|
||||||
private byte[] secretKeyBytes;
|
private byte[] secretKeyBytes;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化方法
|
||||||
|
* <p>
|
||||||
|
* 对签名密钥进行 Base64 编码
|
||||||
|
*/
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
protected void init() {
|
protected void init() {
|
||||||
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
|
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
|
||||||
@@ -53,27 +61,25 @@ public class JwtTokenProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建Token
|
* 创建Token
|
||||||
|
* <p>
|
||||||
|
* 认证成功后的用户信息会被封装到 Authentication 对象中,然后通过 JwtTokenProvider#createToken(Authentication) 方法创建 Token 字符串
|
||||||
*
|
*
|
||||||
* @param authentication
|
* @param authentication 用户认证信息
|
||||||
* @return
|
* @return Token 字符串
|
||||||
*/
|
*/
|
||||||
public String createToken(Authentication authentication) {
|
public String createToken(Authentication authentication) {
|
||||||
Claims claims = Jwts.claims().setSubject(authentication.getName());
|
Claims claims = Jwts.claims().setSubject(authentication.getName());
|
||||||
|
|
||||||
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
|
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
|
||||||
claims.put("userId", userDetails.getUserId());
|
claims.put(JwtClaimConstants.USER_ID, userDetails.getUserId()); // 用户ID
|
||||||
claims.put("username", claims.getSubject());
|
claims.put(JwtClaimConstants.USERNAME, claims.getSubject()); // 用户名
|
||||||
claims.put("deptId", userDetails.getDeptId());
|
claims.put(JwtClaimConstants.DEPT_ID, userDetails.getDeptId()); // 部门ID
|
||||||
claims.put("dataScope", userDetails.getDataScope());
|
claims.put(JwtClaimConstants.DATA_SCOPE, userDetails.getDataScope()); // 数据权限范围
|
||||||
|
|
||||||
|
// claims 中添加角色信息
|
||||||
Set<String> roles = userDetails.getAuthorities().stream()
|
Set<String> roles = userDetails.getAuthorities().stream()
|
||||||
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
|
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
|
||||||
claims.put("authorities", roles);
|
claims.put(JwtClaimConstants.AUTHORITIES, roles);
|
||||||
|
|
||||||
// 权限数据多放入Redis
|
|
||||||
Set<String> perms = userDetails.getPerms();
|
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.USER_PERMS_CACHE_PREFIX + userDetails.getUserId(), perms);
|
|
||||||
|
|
||||||
|
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
Date expirationTime = new Date(now.getTime() + expiration * 1000L);
|
Date expirationTime = new Date(now.getTime() + expiration * 1000L);
|
||||||
@@ -84,47 +90,82 @@ public class JwtTokenProvider {
|
|||||||
.signWith(Keys.hmacShaKeyFor(getSecretKeyBytes()), SignatureAlgorithm.HS256).compact();
|
.signWith(Keys.hmacShaKeyFor(getSecretKeyBytes()), SignatureAlgorithm.HS256).compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据给定的令牌解析出用户认证信息
|
||||||
|
*
|
||||||
|
* @param token JWT Token
|
||||||
|
* @return 用户认证信息
|
||||||
|
*/
|
||||||
public Authentication getAuthentication(String token) {
|
public Authentication getAuthentication(String token) {
|
||||||
Claims claims = this.getTokenClaims(token);
|
Claims claims = this.getTokenClaims(token);
|
||||||
|
|
||||||
SysUserDetails userDetails = new SysUserDetails();
|
SysUserDetails userDetails = new SysUserDetails();
|
||||||
userDetails.setUserId(Convert.toLong(claims.get("userId"))); // 用户ID
|
userDetails.setUserId(Convert.toLong(claims.get(JwtClaimConstants.USER_ID))); // 用户ID
|
||||||
userDetails.setUsername(Convert.toStr(claims.get("username"))); // 用户名
|
userDetails.setUsername(Convert.toStr(claims.get(JwtClaimConstants.USERNAME))); // 用户名
|
||||||
userDetails.setDeptId(Convert.toLong(claims.get("deptId"))); // 部门ID
|
userDetails.setDeptId(Convert.toLong(claims.get(JwtClaimConstants.DEPT_ID))); // 部门ID
|
||||||
userDetails.setDataScope(Convert.toInt(claims.get("dataScope"))); // 数据权限范围
|
userDetails.setDataScope(Convert.toInt(claims.get(JwtClaimConstants.DATA_SCOPE))); // 数据权限范围
|
||||||
|
|
||||||
List<SimpleGrantedAuthority> authorities = ((ArrayList<String>) claims.get("authorities"))
|
// 角色集合
|
||||||
|
Set<SimpleGrantedAuthority> authorities = ((Set<String>) claims.get(JwtClaimConstants.AUTHORITIES))
|
||||||
.stream()
|
.stream()
|
||||||
.map(SimpleGrantedAuthority::new)
|
.map(SimpleGrantedAuthority::new)
|
||||||
.toList();
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
|
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求头中获取Token
|
||||||
|
*
|
||||||
|
* @param req 请求对象
|
||||||
|
* @return Token 字符串
|
||||||
|
*/
|
||||||
public String resolveToken(HttpServletRequest req) {
|
public String resolveToken(HttpServletRequest req) {
|
||||||
String bearerToken = req.getHeader("Authorization");
|
String bearerToken = req.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||||
return bearerToken.substring(7);
|
return bearerToken.substring(7);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验Token是否有效
|
||||||
|
*
|
||||||
|
* @param token JWT Token
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
public boolean validateToken(String token) {
|
public boolean validateToken(String token) {
|
||||||
Jwts.parserBuilder().setSigningKey(getSecretKeyBytes()).build().parseClaimsJws(token);
|
Jwts.parserBuilder().setSigningKey(getSecretKeyBytes()).build().parseClaimsJws(token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token中的用户名
|
||||||
|
*
|
||||||
|
* @param token Token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public String getUsername(String token) {
|
public String getUsername(String token) {
|
||||||
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
|
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token的Claims,claims中包含了用户的基本信息
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public Claims getTokenClaims(String token) {
|
public Claims getTokenClaims(String token) {
|
||||||
Claims claims = Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build().parseClaimsJws(token).getBody();
|
return Jwts.parserBuilder().setSigningKey(this.getSecretKeyBytes()).build().parseClaimsJws(token).getBody();
|
||||||
return claims;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取签名密钥的字节数组
|
||||||
|
*
|
||||||
|
* @return 签名密钥的字节数组
|
||||||
|
*/
|
||||||
public byte[] getSecretKeyBytes() {
|
public byte[] getSecretKeyBytes() {
|
||||||
if (secretKeyBytes == null) {
|
if (secretKeyBytes == null) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.youlai.system.model.dto.UserAuthInfo;
|
import com.youlai.system.model.dto.UserAuthInfo;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@@ -14,11 +15,13 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring Security
|
* Spring Security 用户对象
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
public class SysUserDetails implements UserDetails {
|
public class SysUserDetails implements UserDetails {
|
||||||
|
|
||||||
private Long userId;
|
private Long userId;
|
||||||
@@ -37,10 +40,6 @@ public class SysUserDetails implements UserDetails {
|
|||||||
|
|
||||||
private Integer dataScope;
|
private Integer dataScope;
|
||||||
|
|
||||||
public SysUserDetails() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public SysUserDetails(UserAuthInfo user) {
|
public SysUserDetails(UserAuthInfo user) {
|
||||||
this.userId = user.getUserId();
|
this.userId = user.getUserId();
|
||||||
Set<String> roles = user.getRoles();
|
Set<String> roles = user.getRoles();
|
||||||
|
|||||||
@@ -2,14 +2,18 @@ package com.youlai.system.core.security.service;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.CacheConstants;
|
||||||
import com.youlai.system.common.util.SecurityUtils;
|
import com.youlai.system.common.util.SecurityUtils;
|
||||||
|
import com.youlai.system.model.bo.RolePermsBO;
|
||||||
|
import com.youlai.system.service.SysRoleMenuService;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.PatternMatchUtils;
|
import org.springframework.util.PatternMatchUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,15 +29,17 @@ public class PermissionService {
|
|||||||
|
|
||||||
private final RedisTemplate redisTemplate;
|
private final RedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
private final SysRoleMenuService roleMenuService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断当前登录用户是否拥有操作权限
|
* 判断当前登录用户是否拥有操作权限
|
||||||
*
|
*
|
||||||
* @param perm 权限标识(eg: sys:user:add)
|
* @param requiredPerm 权限标识(eg: sys:user:add)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean hasPerm(String perm) {
|
public boolean hasPerm(String requiredPerm) {
|
||||||
|
|
||||||
if (StrUtil.isBlank(perm)) {
|
if (StrUtil.isBlank(requiredPerm)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 超级管理员放行
|
// 超级管理员放行
|
||||||
@@ -41,21 +47,78 @@ public class PermissionService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
Set<String> roleCodes = SecurityUtils.getRoles();
|
||||||
|
if (CollectionUtil.isEmpty(roleCodes)) {
|
||||||
Set<String> perms = (Set<String>) redisTemplate.opsForValue().get(SecurityConstants.USER_PERMS_CACHE_PREFIX + userId); // 权限数据用户登录成功节点存入redis,详见 JwtTokenManager#createToken()
|
|
||||||
|
|
||||||
if (CollectionUtil.isEmpty(perms)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean hasPermission = perms.stream()
|
boolean hasPermission = false;
|
||||||
.anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item)); // *号匹配任意字符
|
for (String roleCode : roleCodes) {
|
||||||
|
Set<String> rolePerms = (Set<String>) redisTemplate.opsForHash().get(CacheConstants.ROLE_PERMS_PREFIX, roleCode);
|
||||||
|
|
||||||
|
if (CollectionUtil.isEmpty(rolePerms)) {
|
||||||
|
// 无权限 ,判断下一个角色是否有权限
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 匹配权限,支持通配符
|
||||||
|
hasPermission = rolePerms.stream()
|
||||||
|
.anyMatch(rolePerm ->
|
||||||
|
//rolePerm=sys:user:* requiredPerm=sys:user:add 返回true
|
||||||
|
PatternMatchUtils.simpleMatch(rolePerm, requiredPerm)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasPermission) {
|
||||||
|
// 匹配到权限,退出循环
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
log.error("用户无访问权限");
|
log.error("用户无访问权限");
|
||||||
}
|
}
|
||||||
return hasPermission;
|
return hasPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化权限缓存
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void initPermissionCache() {
|
||||||
|
refreshPermissionCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新权限缓存
|
||||||
|
*/
|
||||||
|
public void refreshPermissionCache() {
|
||||||
|
// 清理权限缓存
|
||||||
|
redisTemplate.opsForHash().delete(CacheConstants.ROLE_PERMS_PREFIX, "*");
|
||||||
|
|
||||||
|
List<RolePermsBO> list = roleMenuService.getRolePermsList(null);
|
||||||
|
if (CollectionUtil.isNotEmpty(list)) {
|
||||||
|
list.forEach(item -> {
|
||||||
|
String roleCode = item.getRoleCode();
|
||||||
|
Set<String> perms = item.getPerms();
|
||||||
|
redisTemplate.opsForHash().put(CacheConstants.ROLE_PERMS_PREFIX, roleCode, perms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新权限缓存
|
||||||
|
*/
|
||||||
|
public void refreshPermissionCache(String roleCode) {
|
||||||
|
// 清理权限缓存
|
||||||
|
redisTemplate.opsForHash().delete(CacheConstants.ROLE_PERMS_PREFIX, roleCode);
|
||||||
|
|
||||||
|
List<RolePermsBO> list = roleMenuService.getRolePermsList(roleCode);
|
||||||
|
if (CollectionUtil.isNotEmpty(list)) {
|
||||||
|
RolePermsBO rolePerms = list.get(0);
|
||||||
|
if (rolePerms == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> perms = rolePerms.getPerms();
|
||||||
|
redisTemplate.opsForHash().put(CacheConstants.ROLE_PERMS_PREFIX, roleCode, perms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.youlai.system.filter;
|
|||||||
import cn.hutool.captcha.generator.MathGenerator;
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import com.youlai.system.common.constant.CacheConstants;
|
||||||
import com.youlai.system.common.constant.SecurityConstants;
|
import com.youlai.system.common.constant.SecurityConstants;
|
||||||
import com.youlai.system.common.result.ResultCode;
|
import com.youlai.system.common.result.ResultCode;
|
||||||
import com.youlai.system.common.util.ResponseUtils;
|
import com.youlai.system.common.util.ResponseUtils;
|
||||||
@@ -44,7 +45,7 @@ public class VerifyCodeFilter extends OncePerRequestFilter {
|
|||||||
// 缓存中的验证码
|
// 缓存中的验证码
|
||||||
StringRedisTemplate redisTemplate = SpringUtil.getBean("stringRedisTemplate", StringRedisTemplate.class);
|
StringRedisTemplate redisTemplate = SpringUtil.getBean("stringRedisTemplate", StringRedisTemplate.class);
|
||||||
String verifyCodeKey = request.getParameter(CAPTCHA_KEY_PARAM_NAME);
|
String verifyCodeKey = request.getParameter(CAPTCHA_KEY_PARAM_NAME);
|
||||||
String cacheVerifyCode = redisTemplate.opsForValue().get(SecurityConstants.CAPTCHA_CODE_CACHE_PREFIX + verifyCodeKey);
|
String cacheVerifyCode = redisTemplate.opsForValue().get(CacheConstants.CAPTCHA_CODE_PREFIX + verifyCodeKey);
|
||||||
if (cacheVerifyCode == null) {
|
if (cacheVerifyCode == null) {
|
||||||
ResponseUtils.writeErrMsg(response, ResultCode.VERIFY_CODE_TIMEOUT);
|
ResponseUtils.writeErrMsg(response, ResultCode.VERIFY_CODE_TIMEOUT);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.youlai.system.mapper;
|
package com.youlai.system.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.youlai.system.model.bo.RolePermsBO;
|
||||||
import com.youlai.system.model.entity.SysRoleMenu;
|
import com.youlai.system.model.entity.SysRoleMenu;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色菜单持久层
|
* 角色菜单访问层
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author haoxr
|
||||||
* @since 2022/6/4
|
* @since 2022/6/4
|
||||||
@@ -18,8 +19,13 @@ public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
|
|||||||
/**
|
/**
|
||||||
* 获取角色拥有的菜单ID集合
|
* 获取角色拥有的菜单ID集合
|
||||||
*
|
*
|
||||||
* @param roleId
|
* @param roleId 角色ID
|
||||||
* @return
|
* @return 菜单ID集合
|
||||||
*/
|
*/
|
||||||
List<Long> listMenuIdsByRoleId(Long roleId);
|
List<Long> listMenuIdsByRoleId(Long roleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取权限和拥有权限的角色列表
|
||||||
|
*/
|
||||||
|
List<RolePermsBO> getRolePermsList(String roleCode);
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main/java/com/youlai/system/model/bo/RolePermsBO.java
Normal file
26
src/main/java/com/youlai/system/model/bo/RolePermsBO.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.youlai.system.model.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限业务对象
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/29
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RolePermsBO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色编码
|
||||||
|
*/
|
||||||
|
private String roleCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限标识集合
|
||||||
|
*/
|
||||||
|
private Set<String> perms;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.youlai.system.plugin.captcha;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
|
import cn.hutool.captcha.CircleCaptcha;
|
||||||
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码自动装配配置
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CaptchaConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CaptchaProperties captchaProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码文字生成器
|
||||||
|
*
|
||||||
|
* @return CodeGenerator
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CodeGenerator captchaGenerator() {
|
||||||
|
String codeType = captchaProperties.getCode().getType();
|
||||||
|
int codeLength = captchaProperties.getCode().getLength();
|
||||||
|
if ("math".equalsIgnoreCase(codeType)) {
|
||||||
|
return new MathGenerator(codeLength);
|
||||||
|
} else if ("random".equalsIgnoreCase(codeType)) {
|
||||||
|
return new RandomGenerator(codeLength);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid captcha generator type: " + codeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类
|
||||||
|
*
|
||||||
|
* @return AbstractCaptcha
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public AbstractCaptcha abstractCaptcha() {
|
||||||
|
AbstractCaptcha captcha = null;
|
||||||
|
|
||||||
|
String type = captchaProperties.getType();
|
||||||
|
int width = captchaProperties.getWidth();
|
||||||
|
int height = captchaProperties.getHeight();
|
||||||
|
int interfereCount = captchaProperties.getInterfereCount();
|
||||||
|
int codeLength = captchaProperties.getCode().getLength();
|
||||||
|
|
||||||
|
|
||||||
|
if ("circle".equalsIgnoreCase(type)) {
|
||||||
|
captcha = new CircleCaptcha(width, height, codeLength, interfereCount);
|
||||||
|
} else if ("gif".equalsIgnoreCase(type)) {
|
||||||
|
return null;
|
||||||
|
} else if ("line".equalsIgnoreCase(type)) {
|
||||||
|
return null;
|
||||||
|
} else if ("shear".equalsIgnoreCase(type)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid captcha type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
captcha.setGenerator(captchaGenerator());
|
||||||
|
return captcha;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.youlai.system.plugin.captcha;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码配置
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2023/11/24
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "captcha")
|
||||||
|
@Data
|
||||||
|
public class CaptchaProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型 circle-圆圈干扰验证码|gif-Gif验证码|line-干扰线验证码|shear-扭曲干扰验证码
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码图片宽度
|
||||||
|
*/
|
||||||
|
private int width;
|
||||||
|
/**
|
||||||
|
* 验证码图片高度
|
||||||
|
*/
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 干扰线数量
|
||||||
|
*/
|
||||||
|
private int interfereCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码过期时间,单位:秒
|
||||||
|
*/
|
||||||
|
private Long expireSeconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字符配置
|
||||||
|
*/
|
||||||
|
private CodeProperties code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字体
|
||||||
|
*/
|
||||||
|
private FontProperties font;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字符配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class CodeProperties {
|
||||||
|
/**
|
||||||
|
* 验证码字符类型 math-算术|random-随机字符串
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
/**
|
||||||
|
* 验证码字符长度,type=算术时,表示运算位数(1:个位数 2:十位数);type=随机字符时,表示字符个数
|
||||||
|
*/
|
||||||
|
private int length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码字体配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class FontProperties {
|
||||||
|
/**
|
||||||
|
* 字体名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 字体样式 0-普通|1-粗体|2-斜体
|
||||||
|
*/
|
||||||
|
private int weight;
|
||||||
|
/**
|
||||||
|
* 字体大小
|
||||||
|
*/
|
||||||
|
private int size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -34,6 +34,9 @@ public class DuplicateSubmitAspect {
|
|||||||
|
|
||||||
private final RedissonClient redissonClient;
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT token 工具类
|
||||||
|
*/
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
|
private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
|
|||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(name = "system.config.websocket-enabled")// system.config.websocket-enabled = true 才会自动装配
|
|
||||||
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
@EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
* @author xuxueli 2017-04-28
|
* @author xuxueli 2017-04-28
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
// system.config.xxl-job-enabled = true 才会自动装配
|
@ConditionalOnProperty(name = "xxl.job.enabled") // xxl.job.enabled = true 才会自动装配
|
||||||
@ConditionalOnProperty(name = "system.config.xxl-job-enabled")
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class XxlJobConfig {
|
public class XxlJobConfig {
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,33 @@ package com.youlai.system.service;
|
|||||||
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.youlai.system.model.bo.RolePermsBO;
|
||||||
import com.youlai.system.model.entity.SysRoleMenu;
|
import com.youlai.system.model.entity.SysRoleMenu;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色菜单业务接口
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.5.0
|
||||||
|
*/
|
||||||
public interface SysRoleMenuService extends IService<SysRoleMenu> {
|
public interface SysRoleMenuService extends IService<SysRoleMenu> {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色拥有的菜单ID集合
|
* 获取角色拥有的菜单ID集合
|
||||||
*
|
*
|
||||||
* @param roleId
|
* @param roleId 角色ID
|
||||||
* @return
|
* @return 菜单ID集合
|
||||||
*/
|
*/
|
||||||
List<Long> listMenuIdsByRoleId(Long roleId);
|
List<Long> listMenuIdsByRoleId(Long roleId);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色和权限的列表
|
||||||
|
*
|
||||||
|
* @return 角色权限的列表
|
||||||
|
*/
|
||||||
|
List<RolePermsBO> getRolePermsList(String roleCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
package com.youlai.system.service.impl;
|
package com.youlai.system.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
import cn.hutool.captcha.CircleCaptcha;
|
import cn.hutool.captcha.CircleCaptcha;
|
||||||
|
import cn.hutool.captcha.ICaptcha;
|
||||||
import cn.hutool.captcha.generator.MathGenerator;
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
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.core.security.jwt.JwtTokenProvider;
|
||||||
import com.youlai.system.model.dto.CaptchaResult;
|
import com.youlai.system.model.dto.CaptchaResult;
|
||||||
import com.youlai.system.model.dto.LoginResult;
|
import com.youlai.system.model.dto.LoginResult;
|
||||||
|
import com.youlai.system.plugin.captcha.CaptchaProperties;
|
||||||
import com.youlai.system.service.AuthService;
|
import com.youlai.system.service.AuthService;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@@ -40,6 +44,8 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final StringRedisTemplate redisTemplate;
|
private final StringRedisTemplate redisTemplate;
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
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();
|
Date expiration = claims.getExpiration();
|
||||||
if (expiration != null) {
|
if (expiration != null) {
|
||||||
long ttl = expiration.getTime() - System.currentTimeMillis();
|
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 {
|
} else {
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
|
redisTemplate.opsForValue().set(CacheConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
@@ -88,18 +94,13 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CaptchaResult getCaptcha() {
|
public CaptchaResult getCaptcha() {
|
||||||
|
String captchaCode = abstractCaptcha.getCode(); // 验证码
|
||||||
MathGenerator mathGenerator=new MathGenerator(1);
|
String captchaBase64 = abstractCaptcha.getImageBase64Data(); // 验证码图片Base64
|
||||||
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
|
|
||||||
|
|
||||||
// 验证码文本缓存至Redis,用于登录校验
|
// 验证码文本缓存至Redis,用于登录校验
|
||||||
String captchaKey = IdUtil.fastSimpleUUID();
|
String captchaKey = IdUtil.fastSimpleUUID();
|
||||||
redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_CACHE_PREFIX + captchaKey, captchaCode,
|
redisTemplate.opsForValue().set(CacheConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode,
|
||||||
120, TimeUnit.SECONDS);
|
captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);
|
||||||
|
|
||||||
return CaptchaResult.builder()
|
return CaptchaResult.builder()
|
||||||
.captchaKey(captchaKey)
|
.captchaKey(captchaKey)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.youlai.system.service.impl;
|
package com.youlai.system.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -17,7 +18,7 @@ import com.youlai.system.service.SysDeptService;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -36,7 +37,7 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
|||||||
private final DeptConverter deptConverter;
|
private final DeptConverter deptConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门列表
|
* 获取部门列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<DeptVO> listDepartments(DeptQuery queryParams) {
|
public List<DeptVO> listDepartments(DeptQuery queryParams) {
|
||||||
@@ -52,29 +53,33 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
|||||||
.orderByAsc(SysDept::getSort)
|
.orderByAsc(SysDept::getSort)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (CollectionUtil.isEmpty(deptList)) {
|
||||||
|
return Collections.EMPTY_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有部门ID
|
||||||
Set<Long> deptIds = deptList.stream()
|
Set<Long> deptIds = deptList.stream()
|
||||||
.map(SysDept::getId)
|
.map(SysDept::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
// 获取父节点ID
|
||||||
Set<Long> parentIds = deptList.stream()
|
Set<Long> parentIds = deptList.stream()
|
||||||
.map(SysDept::getParentId)
|
.map(SysDept::getParentId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
// 获取根节点ID(递归的起点),即父节点ID中不包含在部门ID中的节点,注意这里不能拿顶级部门 O 作为根节点,因为部门筛选的时候 O 会被过滤掉
|
||||||
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
|
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
|
||||||
|
|
||||||
List<DeptVO> list = new ArrayList<>();
|
// 递归生成部门树形列表
|
||||||
for (Long rootId : rootIds) {
|
return rootIds.stream()
|
||||||
list.addAll(recurDeptList(rootId, deptList));
|
.flatMap(rootId -> recurDeptList(rootId, deptList).stream())
|
||||||
}
|
.toList();
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归生成部门树形列表
|
* 递归生成部门树形列表
|
||||||
*
|
*
|
||||||
* @param parentId
|
* @param parentId 父ID
|
||||||
* @param deptList
|
* @param deptList 部门列表
|
||||||
* @return
|
* @return 部门树形列表
|
||||||
*/
|
*/
|
||||||
public List<DeptVO> recurDeptList(Long parentId, List<SysDept> deptList) {
|
public List<DeptVO> recurDeptList(Long parentId, List<SysDept> deptList) {
|
||||||
return deptList.stream()
|
return deptList.stream()
|
||||||
@@ -100,54 +105,93 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
|||||||
.select(SysDept::getId, SysDept::getParentId, SysDept::getName)
|
.select(SysDept::getId, SysDept::getParentId, SysDept::getName)
|
||||||
.orderByAsc(SysDept::getSort)
|
.orderByAsc(SysDept::getSort)
|
||||||
);
|
);
|
||||||
|
if (CollectionUtil.isEmpty(deptList)) {
|
||||||
Set<Long> parentIds = deptList.stream()
|
return Collections.EMPTY_LIST;
|
||||||
.map(SysDept::getParentId)
|
}
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
Set<Long> deptIds = deptList.stream()
|
Set<Long> deptIds = deptList.stream()
|
||||||
.map(SysDept::getId)
|
.map(SysDept::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Set<Long> parentIds = deptList.stream()
|
||||||
|
.map(SysDept::getParentId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
|
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);
|
||||||
|
|
||||||
List<Option> list = new ArrayList<>();
|
// 递归生成部门树形列表
|
||||||
for (Long rootId : rootIds) {
|
return rootIds.stream()
|
||||||
list.addAll(recurDeptTreeOptions(rootId, deptList));
|
.flatMap(rootId -> recurDeptTreeOptions(rootId, deptList).stream())
|
||||||
}
|
.toList();
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增部门
|
||||||
|
*
|
||||||
|
* @param formData 部门表单
|
||||||
|
* @return 部门ID
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Long saveDept(DeptForm formData) {
|
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);
|
SysDept entity = deptConverter.form2Entity(formData);
|
||||||
// 部门路径
|
|
||||||
|
// 生成部门路径(tree_path),格式:父节点tree_path + , + 父节点ID,用于删除部门时级联删除子部门
|
||||||
String treePath = generateDeptTreePath(formData.getParentId());
|
String treePath = generateDeptTreePath(formData.getParentId());
|
||||||
entity.setTreePath(treePath);
|
entity.setTreePath(treePath);
|
||||||
|
|
||||||
// 保存部门并返回部门ID
|
// 保存部门并返回部门ID
|
||||||
this.save(entity);
|
boolean result = this.save(entity);
|
||||||
|
Assert.isTrue(result, "部门保存失败");
|
||||||
|
|
||||||
return entity.getId();
|
return entity.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新部门
|
||||||
|
*
|
||||||
|
* @param deptId 部门ID
|
||||||
|
* @param formData 部门表单
|
||||||
|
* @return 部门ID
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Long updateDept(Long deptId, DeptForm formData) {
|
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
|
// form->entity
|
||||||
SysDept entity = deptConverter.form2Entity(formData);
|
SysDept entity = deptConverter.form2Entity(formData);
|
||||||
entity.setId(deptId);
|
entity.setId(deptId);
|
||||||
// 部门路径
|
|
||||||
|
// 生成部门路径(tree_path),格式:父节点tree_path + , + 父节点ID,用于删除部门时级联删除子部门
|
||||||
String treePath = generateDeptTreePath(formData.getParentId());
|
String treePath = generateDeptTreePath(formData.getParentId());
|
||||||
entity.setTreePath(treePath);
|
entity.setTreePath(treePath);
|
||||||
|
|
||||||
// 保存部门并返回部门ID
|
// 保存部门并返回部门ID
|
||||||
this.updateById(entity);
|
boolean result = this.updateById(entity);
|
||||||
|
Assert.isTrue(result, "部门更新失败");
|
||||||
|
|
||||||
return entity.getId();
|
return entity.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归生成部门表格层级列表
|
* 递归生成部门表格层级列表
|
||||||
*
|
*
|
||||||
* @param parentId
|
* @param parentId 父ID
|
||||||
* @param deptList
|
* @param deptList 部门列表
|
||||||
* @return
|
* @return 部门表格层级列表
|
||||||
*/
|
*/
|
||||||
public static List<Option> recurDeptTreeOptions(long parentId, List<SysDept> deptList) {
|
public static List<Option> recurDeptTreeOptions(long parentId, List<SysDept> deptList) {
|
||||||
List<Option> list = CollectionUtil.emptyIfNull(deptList).stream()
|
List<Option> list = CollectionUtil.emptyIfNull(deptList).stream()
|
||||||
@@ -169,7 +213,7 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
|||||||
* 删除部门
|
* 删除部门
|
||||||
*
|
*
|
||||||
* @param ids 部门ID,多个以英文逗号,拼接字符串
|
* @param ids 部门ID,多个以英文逗号,拼接字符串
|
||||||
* @return
|
* @return 是否删除成功
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteByIds(String ids) {
|
public boolean deleteByIds(String ids) {
|
||||||
@@ -189,8 +233,8 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
|||||||
/**
|
/**
|
||||||
* 获取部门详情
|
* 获取部门详情
|
||||||
*
|
*
|
||||||
* @param deptId
|
* @param deptId 部门ID
|
||||||
* @return
|
* @return 部门表单对象
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public DeptForm getDeptForm(Long deptId) {
|
public DeptForm getDeptForm(Long deptId) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.youlai.system.service.impl;
|
package com.youlai.system.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.NumberUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.constant.SystemConstants;
|
||||||
import com.youlai.system.common.enums.MenuTypeEnum;
|
import com.youlai.system.common.enums.MenuTypeEnum;
|
||||||
import com.youlai.system.common.enums.StatusEnum;
|
import com.youlai.system.common.enums.StatusEnum;
|
||||||
|
import com.youlai.system.common.model.Option;
|
||||||
import com.youlai.system.converter.MenuConverter;
|
import com.youlai.system.converter.MenuConverter;
|
||||||
import com.youlai.system.mapper.SysMenuMapper;
|
import com.youlai.system.mapper.SysMenuMapper;
|
||||||
import com.youlai.system.model.bo.RouteBO;
|
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.form.MenuForm;
|
||||||
import com.youlai.system.model.query.MenuQuery;
|
import com.youlai.system.model.query.MenuQuery;
|
||||||
import com.youlai.system.model.vo.MenuVO;
|
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.model.vo.RouteVO;
|
||||||
import com.youlai.system.service.SysMenuService;
|
import com.youlai.system.service.SysMenuService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -30,8 +29,6 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
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
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
|
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
|
||||||
|
|
||||||
private final MenuConverter menuConverter;
|
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())
|
.like(StrUtil.isNotBlank(queryParams.getKeywords()), SysMenu::getName, queryParams.getKeywords())
|
||||||
.orderByAsc(SysMenu::getSort)
|
.orderByAsc(SysMenu::getSort)
|
||||||
);
|
);
|
||||||
|
// 获取所有菜单ID
|
||||||
Set<Long> parentIds = menus.stream()
|
|
||||||
.map(SysMenu::getParentId)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<Long> menuIds = menus.stream()
|
Set<Long> menuIds = menus.stream()
|
||||||
.map(SysMenu::getId)
|
.map(SysMenu::getId)
|
||||||
.collect(Collectors.toSet());
|
.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()
|
List<Long> rootIds = parentIds.stream()
|
||||||
.filter(id -> !menuIds.contains(id))
|
.filter(id -> !menuIds.contains(id))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// 使用递归函数来构建菜单树
|
// 使用递归函数来构建菜单树
|
||||||
List<MenuVO> menuList = rootIds.stream()
|
return rootIds.stream()
|
||||||
.flatMap(rootId -> buildMenuTree(rootId, menus).stream())
|
.flatMap(rootId -> buildMenuTree(rootId, menus).stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
return menuList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +98,8 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<Option> listMenuOptions() {
|
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);
|
return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,35 +251,33 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色权限集合
|
* 获取角色权限(Code)集合
|
||||||
*
|
*
|
||||||
* @param roles
|
* @param roles 角色Code集合
|
||||||
* @return
|
* @return 权限集合
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<String> listRolePerms(Set<String> roles) {
|
public Set<String> listRolePerms(Set<String> roles) {
|
||||||
Set<String> perms = this.baseMapper.listRolePerms(roles);
|
return this.baseMapper.listRolePerms(roles);
|
||||||
return perms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取菜单表单数据
|
* 获取菜单表单数据
|
||||||
*
|
*
|
||||||
* @param id 菜单ID
|
* @param id 菜单ID
|
||||||
* @return
|
* @return 菜单表单数据
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public MenuForm getMenuForm(Long id) {
|
public MenuForm getMenuForm(Long id) {
|
||||||
SysMenu entity = this.getById(id);
|
SysMenu entity = this.getById(id);
|
||||||
MenuForm menuForm = menuConverter.entity2Form(entity);
|
return menuConverter.entity2Form(entity);
|
||||||
return menuForm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除菜单
|
* 删除菜单
|
||||||
*
|
*
|
||||||
* @param id 菜单ID
|
* @param id 菜单ID
|
||||||
* @return
|
* @return 是否删除成功
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteMenu(Long id) {
|
public boolean deleteMenu(Long id) {
|
||||||
@@ -289,6 +287,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
.or()
|
.or()
|
||||||
.apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", id));
|
.apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", id));
|
||||||
}
|
}
|
||||||
|
// 无异常即为删除成功
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,28 @@ package com.youlai.system.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.youlai.system.mapper.SysRoleMenuMapper;
|
import com.youlai.system.mapper.SysRoleMenuMapper;
|
||||||
|
import com.youlai.system.model.bo.RolePermsBO;
|
||||||
import com.youlai.system.model.entity.SysRoleMenu;
|
import com.youlai.system.model.entity.SysRoleMenu;
|
||||||
import com.youlai.system.service.SysRoleMenuService;
|
import com.youlai.system.service.SysRoleMenuService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色菜单业务实现
|
||||||
|
*
|
||||||
|
* @author haoxr
|
||||||
|
* @since 2.5.0
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
|
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色拥有的菜单ID集合
|
* 获取角色拥有的菜单ID集合
|
||||||
*
|
*
|
||||||
* @param roleId
|
* @param roleId 角色ID
|
||||||
* @return
|
* @return 菜单ID集合
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<Long> listMenuIdsByRoleId(Long roleId) {
|
public List<Long> listMenuIdsByRoleId(Long roleId) {
|
||||||
@@ -23,4 +31,15 @@ public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRo
|
|||||||
return menuIds;
|
return menuIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取权限角色列表
|
||||||
|
*
|
||||||
|
* @return 权限角色列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<RolePermsBO> getRolePermsList(String roleCode) {
|
||||||
|
List<RolePermsBO> rolePerms= this.baseMapper.getRolePermsList(roleCode);
|
||||||
|
return rolePerms;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
/**
|
/**
|
||||||
* 角色分页列表
|
* 角色分页列表
|
||||||
*
|
*
|
||||||
* @param queryParams
|
* @param queryParams 角色查询参数
|
||||||
* @return
|
* @return {@link Page<RolePageVO>} – 角色分页列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Page<RolePageVO> getRolePage(RolePageQuery queryParams) {
|
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 roleConverter.entity2Page(rolePage);
|
||||||
return pageResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色下拉列表
|
* 角色下拉列表
|
||||||
*
|
*
|
||||||
* @return
|
* @return {@link List<Option>} – 角色下拉列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<Option> listRoleOptions() {
|
public List<Option> listRoleOptions() {
|
||||||
@@ -88,15 +87,14 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 实体转换
|
// 实体转换
|
||||||
List<Option> list = roleConverter.entities2Options(roleList);
|
return roleConverter.entities2Options(roleList);
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存角色
|
* 保存角色
|
||||||
*
|
*
|
||||||
* @param roleForm
|
* @param roleForm 角色表单数据
|
||||||
* @return
|
* @return {@link Boolean}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean saveRole(RoleForm roleForm) {
|
public boolean saveRole(RoleForm roleForm) {
|
||||||
@@ -114,8 +112,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
// 实体转换
|
// 实体转换
|
||||||
SysRole role = roleConverter.form2Entity(roleForm);
|
SysRole role = roleConverter.form2Entity(roleForm);
|
||||||
|
|
||||||
boolean result = this.saveOrUpdate(role);
|
return this.saveOrUpdate(role);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,8 +124,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
@Override
|
@Override
|
||||||
public RoleForm getRoleForm(Long roleId) {
|
public RoleForm getRoleForm(Long roleId) {
|
||||||
SysRole entity = this.getById(roleId);
|
SysRole entity = this.getById(roleId);
|
||||||
RoleForm roleForm = roleConverter.entity2Form(entity);
|
return roleConverter.entity2Form(entity);
|
||||||
return roleForm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,22 +146,22 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
* 批量删除角色
|
* 批量删除角色
|
||||||
*
|
*
|
||||||
* @param ids 角色ID,多个使用英文逗号(,)分割
|
* @param ids 角色ID,多个使用英文逗号(,)分割
|
||||||
* @return
|
* @return {@link Boolean}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteRoles(String ids) {
|
public boolean deleteRoles(String ids) {
|
||||||
List<Long> roleIds = Arrays.asList(ids.split(",")).stream().map(id -> Long.parseLong(id)).collect(Collectors.toList());
|
Assert.isTrue(StrUtil.isNotBlank(ids), "删除的角色ID不能为空");
|
||||||
Optional.ofNullable(roleIds)
|
List<Long> roleIds = Arrays.stream(ids.split(","))
|
||||||
.orElse(new ArrayList<>())
|
.map(Long::parseLong)
|
||||||
.forEach(id -> {
|
.collect(Collectors.toList());
|
||||||
long count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, id));
|
|
||||||
Assert.isTrue(count <= 0, "该角色已分配用户,无法删除");
|
|
||||||
sysRoleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
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 this.removeByIds(roleIds);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,16 +172,15 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<Long> getRoleMenuIds(Long roleId) {
|
public List<Long> getRoleMenuIds(Long roleId) {
|
||||||
List<Long> menuIds = sysRoleMenuService.listMenuIdsByRoleId(roleId);
|
return sysRoleMenuService.listMenuIdsByRoleId(roleId);
|
||||||
return menuIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改角色的资源权限
|
* 修改角色的资源权限
|
||||||
*
|
*
|
||||||
* @param roleId
|
* @param roleId 角色ID
|
||||||
* @param menuIds
|
* @param menuIds 菜单ID集合
|
||||||
* @return
|
* @return {@link Boolean}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -206,8 +201,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
/**
|
/**
|
||||||
* 获取最大范围的数据权限
|
* 获取最大范围的数据权限
|
||||||
*
|
*
|
||||||
* @param roles
|
* @param roles 角色编码集合
|
||||||
* @return
|
* @return {@link Integer} – 数据权限范围
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Integer getMaximumDataScope(Set<String> roles) {
|
public Integer getMaximumDataScope(Set<String> roles) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
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.SecurityConstants;
|
||||||
import com.youlai.system.common.constant.SystemConstants;
|
import com.youlai.system.common.constant.SystemConstants;
|
||||||
import com.youlai.system.common.util.DateUtils;
|
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.UserExportVO;
|
||||||
import com.youlai.system.model.vo.UserInfoVO;
|
import com.youlai.system.model.vo.UserInfoVO;
|
||||||
import com.youlai.system.model.vo.UserPageVO;
|
import com.youlai.system.model.vo.UserPageVO;
|
||||||
import com.youlai.system.service.SysMenuService;
|
import com.youlai.system.service.*;
|
||||||
import com.youlai.system.service.SysRoleService;
|
|
||||||
import com.youlai.system.service.SysUserRoleService;
|
|
||||||
import com.youlai.system.service.SysUserService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
@@ -34,6 +32,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -60,6 +59,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
|
|
||||||
private final RedisTemplate redisTemplate;
|
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
|
@Override
|
||||||
public UserInfoVO getCurrentUserInfo() {
|
public UserInfoVO getCurrentUserInfo() {
|
||||||
@@ -257,9 +258,16 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
userInfoVO.setRoles(roles);
|
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);
|
userInfoVO.setPerms(perms);
|
||||||
|
|
||||||
return userInfoVO;
|
return userInfoVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,28 @@
|
|||||||
WHERE
|
WHERE
|
||||||
rm.role_id = #{roleId}
|
rm.role_id = #{roleId}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 权限和拥有权限的角色集合的Map -->
|
||||||
|
<resultMap id="PremRolesMap" type="com.youlai.system.model.bo.RolePermsBO">
|
||||||
|
<result property="roleCode" column="role_code"/>
|
||||||
|
<collection property="perms" ofType="string" javaType="java.util.Set">
|
||||||
|
<result column="perm"/>
|
||||||
|
</collection>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 获取权限和拥有权限的角色列表 -->
|
||||||
|
<select id="getRolePermsList" resultMap="PremRolesMap">
|
||||||
|
SELECT
|
||||||
|
t2.`code` role_code,
|
||||||
|
t3.perm
|
||||||
|
FROM
|
||||||
|
`sys_role_menu` t1
|
||||||
|
INNER JOIN sys_role t2 ON t1.role_id = t2.id
|
||||||
|
INNER JOIN sys_menu t3 ON t1.menu_id = t3.id
|
||||||
|
WHERE
|
||||||
|
type = '${@com.youlai.system.common.enums.MenuTypeEnum@BUTTON.getValue()}'
|
||||||
|
<if test="roleCode!=null and roleCode.trim() neq ''">
|
||||||
|
AND t2.`code` = #{roleCode}
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user