diff --git a/yudao-admin-server/pom.xml b/yudao-admin-server/pom.xml
index f3b91ac0f..4bd789b5c 100644
--- a/yudao-admin-server/pom.xml
+++ b/yudao-admin-server/pom.xml
@@ -122,6 +122,11 @@
yudao-spring-boot-starter-tenant
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-data-permission
+
+
org.apache.velocity
velocity-engine-core
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/rule/DeptDataPermissionRule.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/rule/DeptDataPermissionRule.java
new file mode 100644
index 000000000..ed5c2b9cf
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/rule/DeptDataPermissionRule.java
@@ -0,0 +1,173 @@
+package cn.iocoder.yudao.adminserver.framework.datapermission.core.rule;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.adminserver.framework.datapermission.core.service.DeptDataPermissionService;
+import cn.iocoder.yudao.adminserver.framework.datapermission.core.service.dto.DeptDataPermissionRespDTO;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.InExpression;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 基于部门的 {@link DataPermissionRule} 数据权限规则实现
+ *
+ * 注意,使用 DeptDataPermissionRule 时,需要保证表中有 dept_id 部门编号的字段,可自定义。
+ *
+ * 实际业务场景下,会存在一个经典的问题?当用户修改部门时,冗余的 dept_id 是否需要修改?
+ * 1. 一般情况下,dept_id 不进行修改,则会导致用户看到之前的数据。【yudao-admin-server 采用该方案】
+ * 2. 部分情况下,希望该用户还是能看到之前的数据,则有两种方式解决:【需要你改造该 DeptDataPermissionRule 的实现代码】
+ * 1)编写洗数据的脚本,将 dept_id 修改成新部门的编号;【建议】
+ * 最终过滤条件是 WHERE dept_id = ?
+ * 2)洗数据的话,可能涉及的数据量较大,也可以采用 user_id 进行过滤的方式,此时需要获取到 dept_id 对应的所有 user_id 用户编号;
+ * 最终过滤条件是 WHERE user_id IN (?, ?, ? ...)
+ * 3)想要保证原 dept_id 和 user_id 都可以看的到,此时使用 dept_id 和 user_id 一起过滤;
+ * 最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...)
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Slf4j
+public class DeptDataPermissionRule implements DataPermissionRule {
+
+ private static final String DEPT_COLUMN_NAME = "dept_id";
+ private static final String USER_COLUMN_NAME = "user_id";
+
+ private final DeptDataPermissionService deptDataPermissionService;
+
+ /**
+ * 基于部门的表字段配置
+ * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
+ *
+ * key:表名
+ * value:字段名
+ */
+ private final Map DEPT_TABLE_CONFIG = new HashMap<>();
+ /**
+ * 基于用户的表字段配置
+ * 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
+ *
+ * key:表名
+ * value:字段名
+ */
+ private final Map USER_TABLE_CONFIG = new HashMap<>();
+ /**
+ * 所有表名,是 {@link #DEPT_TABLE_CONFIG} 和 {@link #USER_TABLE_CONFIG} 的合集
+ */
+ private final Set TABLE_NAMES = new HashSet<>();
+
+ @Override
+ public Set getTableNames() {
+ return TABLE_NAMES;
+ }
+
+ @Override
+ public Expression getExpression(String tableName, Alias tableAlias) {
+ // 只有有登陆用户的情况下,才进行数据权限的处理
+ LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
+ if (loginUser == null) {
+ return null;
+ }
+
+ // 获得数据权限
+ DeptDataPermissionRespDTO deptDataPermission = deptDataPermissionService.getDeptDataPermission(loginUser);
+ if (deptDataPermission == null) {
+ log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
+ return null;
+ }
+
+ // 情况一,如果是 ALL 可查看全部,则无需拼接条件
+ if (deptDataPermission.getAll()) {
+ return null;
+ }
+
+ // 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
+ if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
+ && Boolean.FALSE.equals(deptDataPermission.getSelf())) {
+ return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
+ }
+
+ // 情况三,拼接 Dept 和 User 的条件,最后组合
+ Expression deptExpression = this.buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());
+ Expression userExpression = this.buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());
+ if (deptExpression == null && userExpression == null) {
+ log.error("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
+ JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission));
+ throw new NullPointerException(String.format("LoginUser(%d) tableName(%s) tableAlias(%s) 构建的条件为空",
+ loginUser.getId(), tableName, tableAlias.getName()));
+ }
+ if (deptExpression == null) {
+ return userExpression;
+ }
+ if (userExpression == null) {
+ return deptExpression;
+ }
+ // 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE dept_id IN ? OR user_id = ?
+ return new OrExpression(deptExpression, userExpression);
+ }
+
+ private Expression buildDeptExpression(String tableName, Alias tableAlias, Set deptIds) {
+ // 如果不存在配置,则无需作为条件
+ String columnName = DEPT_TABLE_CONFIG.get(tableName);
+ if (StrUtil.isEmpty(columnName)) {
+ return null;
+ }
+ // 拼接条件
+ return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
+ new ExpressionList(CollectionUtils.convertList(deptIds, LongValue::new)));
+ }
+
+ private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {
+ // 如果不查看自己,则无需作为条件
+ if (Boolean.FALSE.equals(self)) {
+ return null;
+ }
+ String columnName = USER_TABLE_CONFIG.get(tableName);
+ if (StrUtil.isEmpty(columnName)) {
+ return null;
+ }
+ // 拼接条件
+ return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
+ }
+
+ // ==================== 添加配置 ====================
+
+ public void addDeptTableConfig(Class extends BaseDO> entityClass) {
+ addDeptTableConfig(entityClass, DEPT_COLUMN_NAME);
+ }
+
+ public void addDeptTableConfig(Class extends BaseDO> entityClass, String columnName) {
+ String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
+ DEPT_TABLE_CONFIG.put(tableName, columnName);
+ TABLE_NAMES.add(tableName);
+ }
+
+ public void addUserTableConfig(Class extends BaseDO> entityClass) {
+ addUserTableConfig(entityClass, DEPT_COLUMN_NAME);
+ }
+
+ public void addUserTableConfig(Class extends BaseDO> entityClass, String columnName) {
+ String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
+ USER_TABLE_CONFIG.put(tableName, columnName);
+ TABLE_NAMES.add(tableName);
+ }
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/DeptDataPermissionService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/DeptDataPermissionService.java
new file mode 100644
index 000000000..c7e44437f
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/DeptDataPermissionService.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.adminserver.framework.datapermission.core.service;
+
+import cn.iocoder.yudao.adminserver.framework.datapermission.core.service.dto.DeptDataPermissionRespDTO;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+
+/**
+ * 基于部门的数据权限 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface DeptDataPermissionService {
+
+ /**
+ * 获得登陆用户的部门数据权限
+ *
+ * @param loginUser 登陆用户
+ * @return 部门数据权限
+ */
+ DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser);
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/dto/DeptDataPermissionRespDTO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/dto/DeptDataPermissionRespDTO.java
new file mode 100644
index 000000000..3aa3aba51
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/dto/DeptDataPermissionRespDTO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.adminserver.framework.datapermission.core.service.dto;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 部门的数据权限 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class DeptDataPermissionRespDTO {
+
+ /**
+ * 是否可查看全部数据
+ */
+ private Boolean all;
+ /**
+ * 是否可查看自己的数据
+ */
+ private Boolean self;
+ /**
+ * 可查看的部门编号数组
+ */
+ private Set deptIds;
+
+ public DeptDataPermissionRespDTO() {
+ this.all = false;
+ this.self = false;
+ this.deptIds = new HashSet<>();
+ }
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/impl/DeptDataPermissionServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/impl/DeptDataPermissionServiceImpl.java
new file mode 100644
index 000000000..b02c7e239
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/datapermission/core/service/impl/DeptDataPermissionServiceImpl.java
@@ -0,0 +1,88 @@
+package cn.iocoder.yudao.adminserver.framework.datapermission.core.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.adminserver.framework.datapermission.core.service.DeptDataPermissionService;
+import cn.iocoder.yudao.adminserver.framework.datapermission.core.service.dto.DeptDataPermissionRespDTO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO;
+import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
+import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 基于部门的数据权限 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class DeptDataPermissionServiceImpl implements DeptDataPermissionService {
+
+ /**
+ * LoginUser 的 Context 缓存 Key
+ */
+ private static final String CONTEXT_KEY = DeptDataPermissionServiceImpl.class.getSimpleName();
+
+ private final SysRoleService roleService;
+ private final SysDeptService deptService;
+
+ @Override
+ public DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser) {
+ // 判断是否 context 已经缓存
+ DeptDataPermissionRespDTO result = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
+ if (result != null) {
+ return result;
+ }
+
+ // 创建 DeptDataPermissionRespDTO 对象
+ result = new DeptDataPermissionRespDTO();
+ List roles = roleService.getRolesFromCache(loginUser.getRoleIds());
+ for (SysRoleDO role : roles) {
+ // 为空时,跳过
+ if (role.getDataScope() == null) {
+ continue;
+ }
+ // 情况一,ALL
+ if (Objects.equals(role.getDataScope(), DataScopeEnum.ALL.getScope())) {
+ result.setAll(true);
+ continue;
+ }
+ // 情况二,DEPT_CUSTOM
+ if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_CUSTOM.getScope())) {
+ CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds());
+ continue;
+ }
+ // 情况三,DEPT_ONLY
+ if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) {
+ CollectionUtils.addIfNotNull(result.getDeptIds(), loginUser.getDeptId());
+ continue;
+ }
+ // 情况四,DEPT_DEPT_AND_CHILD
+ if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) {
+ List depts = deptService.getDeptsByParentIdFromCache(loginUser.getDeptId(), true);
+ CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, SysDeptDO::getId));
+ continue;
+ }
+ // 情况五,SELF
+ if (Objects.equals(role.getDataScope(), DataScopeEnum.SELF.getScope())) {
+ result.setSelf(true);
+ continue;
+ }
+ // 未知情况,error log 即可
+ log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", loginUser.getId(), JsonUtils.toJsonString(result));
+ }
+
+ // 添加到缓存,并返回
+ loginUser.setContext(CONTEXT_KEY, result);
+ return null;
+ }
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
index 2804b2813..6060f7e23 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -92,9 +92,7 @@ public class SysAuthServiceImpl implements SysAuthService {
throw new UsernameNotFoundException(username);
}
// 创建 LoginUser 对象
- LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
- loginUser.setPostIds(user.getPostIds());
- return loginUser;
+ return this.buildLoginUser(user);
}
@Override
@@ -107,9 +105,7 @@ public class SysAuthServiceImpl implements SysAuthService {
this.createLoginLog(user.getUsername(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS);
// 创建 LoginUser 对象
- LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
- loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
- return loginUser;
+ return this.buildLoginUser(user);
}
@Override
@@ -117,10 +113,9 @@ public class SysAuthServiceImpl implements SysAuthService {
// 判断验证码是否正确
this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode());
- // 使用账号密码,进行登录。
+ // 使用账号密码,进行登录
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
- loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
- loginUser.setGroups(this.getUserPosts(loginUser.getPostIds()));
+
// 缓存登陆用户到 Redis 中,返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
@@ -234,8 +229,7 @@ public class SysAuthServiceImpl implements SysAuthService {
this.createLoginLog(user.getUsername(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS);
// 创建 LoginUser 对象
- LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
- loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
+ LoginUser loginUser = this.buildLoginUser(user);
// 绑定社交用户(更新)
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
@@ -252,7 +246,6 @@ public class SysAuthServiceImpl implements SysAuthService {
// 使用账号密码,进行登录。
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
- loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 绑定社交用户(新增)
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
@@ -305,15 +298,14 @@ public class SysAuthServiceImpl implements SysAuthService {
return null;
}
// 刷新 LoginUser 缓存
- this.refreshLoginUserCache(token, loginUser);
- return loginUser;
+ return this.refreshLoginUserCache(token, loginUser);
}
- private void refreshLoginUserCache(String token, LoginUser loginUser) {
+ private LoginUser refreshLoginUserCache(String token, LoginUser loginUser) {
// 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存
if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() <
userSessionCoreService.getSessionTimeoutMillis() / 3) {
- return;
+ return loginUser;
}
// 重新加载 SysUserDO 信息
@@ -323,9 +315,18 @@ public class SysAuthServiceImpl implements SysAuthService {
}
// 刷新 LoginUser 缓存
+ LoginUser newLoginUser= this.buildLoginUser(user);
+ userSessionCoreService.refreshUserSession(token, newLoginUser);
+ return newLoginUser;
+ }
+
+ private LoginUser buildLoginUser(SysUserDO user) {
+ LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
+ // 补全字段
loginUser.setDeptId(user.getDeptId());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId()));
- userSessionCoreService.refreshUserSession(token, loginUser);
+ loginUser.setGroups(this.getUserPosts(user.getPostIds()));
+ return loginUser;
}
}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java
index cac9bf565..2eccdf721 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java
@@ -169,9 +169,12 @@ public class SysDeptServiceImpl implements SysDeptService {
@Override
public List getDeptsByParentIdFromCache(Long parentId, boolean recursive) {
- List result = new ArrayList<>();
+ if (parentId == null) {
+ return Collections.emptyList();
+ }
+ List result = new ArrayList<>(); // TODO 芋艿:待优化,新增缓存,避免每次遍历的计算
// 递归,简单粗暴
- this.listDeptsByParentIdFromCache(result, parentId,
+ this.getDeptsByParentIdFromCache(result, parentId,
recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次
parentDeptCache);
return result;
@@ -185,8 +188,8 @@ public class SysDeptServiceImpl implements SysDeptService {
* @param recursiveCount 递归次数
* @param parentDeptMap 父部门 Map,使用缓存,避免变化
*/
- private void listDeptsByParentIdFromCache(List result, Long parentId, int recursiveCount,
- Multimap parentDeptMap) {
+ private void getDeptsByParentIdFromCache(List result, Long parentId, int recursiveCount,
+ Multimap parentDeptMap) {
// 递归次数为 0,结束!
if (recursiveCount == 0) {
return;
@@ -198,7 +201,7 @@ public class SysDeptServiceImpl implements SysDeptService {
}
result.addAll(depts);
// 继续递归
- depts.forEach(dept -> listDeptsByParentIdFromCache(result, dept.getId(),
+ depts.forEach(dept -> getDeptsByParentIdFromCache(result, dept.getId(),
recursiveCount - 1, parentDeptMap));
}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/impl/SysRoleServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/impl/SysRoleServiceImpl.java
index c233f4f85..0e82c5051 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/impl/SysRoleServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/impl/SysRoleServiceImpl.java
@@ -18,6 +18,7 @@ import cn.iocoder.yudao.adminserver.modules.system.enums.permission.SysRoleTypeE
import cn.iocoder.yudao.adminserver.modules.system.mq.producer.permission.SysRoleProducer;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService;
+import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
@@ -127,6 +128,7 @@ public class SysRoleServiceImpl implements SysRoleService {
SysRoleDO role = SysRoleConvert.INSTANCE.convert(reqVO);
role.setType(SysRoleTypeEnum.CUSTOM.getType());
role.setStatus(CommonStatusEnum.ENABLE.getStatus());
+ role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限
roleMapper.insert(role);
// 发送刷新消息
roleProducer.sendRoleRefreshMessage();
diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java
index 492128e6b..11696fe25 100644
--- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java
+++ b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java
@@ -89,7 +89,6 @@ public class SysAuthServiceImplTest extends BaseDbUnitTest {
LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username);
// 校验
AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
- assertNull(loginUser.getRoleIds()); // 此时不会加载角色,所以是空的
}
@Test
diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/SysRoleServiceTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/SysRoleServiceTest.java
index 3b39e976c..08bd6521b 100644
--- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/SysRoleServiceTest.java
+++ b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/permission/SysRoleServiceTest.java
@@ -133,11 +133,11 @@ public class SysRoleServiceTest extends BaseDbUnitTest {
//调用
Set deptIdSet = Arrays.asList(1L, 2L, 3L, 4L, 5L).stream().collect(Collectors.toSet());
- sysRoleService.updateRoleDataScope(roleId, DataScopeEnum.DEPT_CUSTOM.getScore(), deptIdSet);
+ sysRoleService.updateRoleDataScope(roleId, DataScopeEnum.DEPT_CUSTOM.getScope(), deptIdSet);
//断言
SysRoleDO newRoleDO = roleMapper.selectById(roleId);
- assertEquals(DataScopeEnum.DEPT_CUSTOM.getScore(), newRoleDO.getDataScope());
+ assertEquals(DataScopeEnum.DEPT_CUSTOM.getScope(), newRoleDO.getDataScope());
Set newDeptIdSet = newRoleDO.getDataScopeDeptIds();
assertTrue(deptIdSet.size() == newDeptIdSet.size());
@@ -242,7 +242,7 @@ public class SysRoleServiceTest extends BaseDbUnitTest {
o.setCode("code");
o.setType(SysRoleTypeEnum.CUSTOM.getType());
o.setStatus(1);
- o.setDataScope(DataScopeEnum.ALL.getScore());
+ o.setDataScope(DataScopeEnum.ALL.getScope());
});
roleMapper.insert(roleDO);
@@ -293,7 +293,7 @@ public class SysRoleServiceTest extends BaseDbUnitTest {
o.setName(name);
o.setType(typeEnum.getType());
o.setStatus(status);
- o.setDataScope(scopeEnum.getScore());
+ o.setDataScope(scopeEnum.getScope());
o.setCode(code);
});
return roleDO;
diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 980e09705..300635c6b 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -369,6 +369,12 @@
${revision}
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-data-permission
+ ${revision}
+
+
org.projectlombok
lombok
diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java
index 01e4bccdb..44bd502a2 100644
--- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java
@@ -5,11 +5,18 @@ import cn.iocoder.yudao.framework.datapermission.core.db.DataPermissionDatabaseI
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
+import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
+/**
+ * 数据全新啊的自动配置类
+ *
+ * @author 芋道源码
+ */
@Configuration
public class DataPermissionAutoConfiguration {
@@ -19,9 +26,15 @@ public class DataPermissionAutoConfiguration {
}
@Bean
- public DataPermissionDatabaseInterceptor dataPermissionDatabaseInterceptor(List rules) {
+ public DataPermissionDatabaseInterceptor dataPermissionDatabaseInterceptor(MybatisPlusInterceptor interceptor,
+ List rules) {
+ // 创建 DataPermissionDatabaseInterceptor 拦截器
DataPermissionRuleFactory ruleFactory = dataPermissionRuleFactory(rules);
- return new DataPermissionDatabaseInterceptor(ruleFactory);
+ DataPermissionDatabaseInterceptor inner = new DataPermissionDatabaseInterceptor(ruleFactory);
+ // 添加到 interceptor 中
+ // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
+ MyBatisUtils.addInterceptor(interceptor, inner, 0);
+ return inner;
}
@Bean
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
index 91db0b568..3833ee62d 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.security.core;
+import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -28,10 +29,6 @@ public class LoginUser implements UserDetails {
* 关联 {@link UserTypeEnum}
*/
private Integer userType;
- /**
- * 角色编号数组
- */
- private Set roleIds;
/**
* 最后更新时间
*/
@@ -56,7 +53,10 @@ public class LoginUser implements UserDetails {
// ========== UserTypeEnum.ADMIN 独有字段 ==========
// TODO 芋艿:可以通过定义一个 Map exts 的方式,去除管理员的字段。不过这样会导致系统比较复杂,所以暂时不去掉先;
-
+ /**
+ * 角色编号数组
+ */
+ private Set roleIds;
/**
* 部门编号
*/
@@ -71,6 +71,15 @@ public class LoginUser implements UserDetails {
// TODO jason:这个字段,改成 postCodes 明确更好哈
private List groups;
+ // ========== 上下文 ==========
+ /**
+ * 上下文字段,不进行持久化
+ *
+ * 1. 用于基于 LoginUser 维度的临时缓存
+ */
+ @JsonIgnore
+ private Map context;
+
@Override
@JsonIgnore// 避免序列化
public String getPassword() {
@@ -115,4 +124,17 @@ public class LoginUser implements UserDetails {
return true; // 返回 true,不依赖 Spring Security 判断
}
+ // ========== 上下文 ==========
+
+ public void setContext(String key, Object value) {
+ if (context == null) {
+ context = new HashMap<>();
+ }
+ context.put(key, value);
+ }
+
+ public T getContext(String key, Class type) {
+ return MapUtil.get(context, key, type);
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/enums/DataScopeEnum.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/enums/DataScopeEnum.java
index ac3396ce3..c67a526d4 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/enums/DataScopeEnum.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/enums/DataScopeEnum.java
@@ -15,14 +15,16 @@ import lombok.Getter;
public enum DataScopeEnum {
ALL(1), // 全部数据权限
+
DEPT_CUSTOM(2), // 指定部门数据权限
DEPT_ONLY(3), // 部门数据权限
DEPT_AND_CHILD(4), // 部门及以下数据权限
- DEPT_SELF(5); // 仅本人数据权限
+
+ SELF(5); // 仅本人数据权限
/**
* 范围
*/
- private final Integer score;
+ private final Integer scope;
}
diff --git a/更新日志.md b/更新日志.md
index 29f109c3d..14f95b04f 100644
--- a/更新日志.md
+++ b/更新日志.md
@@ -29,6 +29,7 @@
* 【新增】多租户,支持 Web、Security、Job、MQ、Async、DB、Redis 组件
* 【新增】数据权限,内置基于部门过滤的规则
* 【新增】用户前台的昵称、头像的修改
+* 【优化】管理后台的登陆成功后,LoginUser 使用统一方法补全信息
### 🐞 Bug Fixes