fix: 数据权限调整后引发的问题修复

This commit is contained in:
Ray.Hao
2026-02-14 10:56:24 +08:00
parent 8df1252ff8
commit d379e30d3f
9 changed files with 134 additions and 61 deletions

View File

@@ -14,13 +14,8 @@ import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
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 net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import java.lang.reflect.Method;
import java.util.List;
@@ -89,7 +84,7 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
return where;
}
// 使用并集策略过滤
return dataScopeFilterWithUnion(annotation, dataScopes, where);
return dataScopeFilterWithUnion(mappedStatementId, annotation, dataScopes, where);
}
}
return where;
@@ -119,7 +114,8 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
* @param where 原始查询条件
* @return 追加权限过滤后的查询条件
*/
private Expression dataScopeFilterWithUnion(DataPermission annotation, List<RoleDataScope> dataScopes, Expression where) {
@SneakyThrows
private Expression dataScopeFilterWithUnion(String mappedStatementId, DataPermission annotation, List<RoleDataScope> dataScopes, Expression where) {
String deptAlias = annotation.deptAlias();
String deptIdColumnName = annotation.deptIdColumnName();
String userAlias = annotation.userAlias();
@@ -145,13 +141,17 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
}
// 用括号包裹并集条件
Expression finalExpression = new ParenthesedExpressionList<>(unionExpression);
Expression finalExpression = CCJSqlParserUtil.parseCondExpression("(" + unionExpression + ")");
if (where == null) {
log.debug("DataPermission applied. mappedStatementId={}, segment={}", mappedStatementId, finalExpression);
return finalExpression;
}
return new AndExpression(where, finalExpression);
Expression combined = new AndExpression(where, finalExpression);
log.debug("DataPermission applied. mappedStatementId={}, originWhere={}, segment={}, combined={}",
mappedStatementId, where, finalExpression, combined);
return combined;
}
/**
@@ -226,35 +226,15 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
* @param deptId 部门ID
* @return IN 子查询表达式
*/
@SneakyThrows
private Expression buildDeptAndSubExpression(Column deptColumn, Long deptId) {
// 构建子查询: SELECT id FROM sys_dept WHERE id = ? OR FIND_IN_SET(?, tree_path)
PlainSelect subSelectBody = new PlainSelect();
subSelectBody.setFromItem(new Table(DEPT_TABLE));
subSelectBody.addSelectItems(new Column(DEPT_ID_COLUMN));
// WHERE id = ?
EqualsTo idEquals = new EqualsTo();
idEquals.setLeftExpression(new Column(DEPT_ID_COLUMN));
idEquals.setRightExpression(new LongValue(deptId));
// FIND_IN_SET(?, tree_path)
Function findInSet = new Function();
findInSet.setName("FIND_IN_SET");
findInSet.setParameters(new ExpressionList<>(
new LongValue(deptId),
new Column(DEPT_TREE_PATH_COLUMN)
));
// WHERE id = ? OR FIND_IN_SET(?, tree_path)
OrExpression whereClause = new OrExpression(idEquals, findInSet);
subSelectBody.setWhere(whereClause);
// 构建子查询
ParenthesedSelect subSelect = new ParenthesedSelect();
subSelect.setSelect(subSelectBody);
// 构建 IN 表达式
return new InExpression(deptColumn, subSelect);
// 使用字符串解析,避免不同 JSqlParser 版本下 InExpression/ItemsList 渲染差异导致 SQL 语法错误
// SQL: dept_id IN (SELECT id FROM sys_dept WHERE id = ? OR FIND_IN_SET(?, tree_path))
String columnName = deptColumn.toString();
String sql = columnName + " IN (SELECT " + DEPT_ID_COLUMN + " FROM " + DEPT_TABLE +
" WHERE " + DEPT_ID_COLUMN + " = " + deptId +
" OR FIND_IN_SET(" + deptId + ", " + DEPT_TREE_PATH_COLUMN + "))";
return CCJSqlParserUtil.parseCondExpression(sql);
}
/**
@@ -266,6 +246,7 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
* @param customDeptIds 自定义部门ID列表
* @return IN 表达式,如果没有部门则返回 1=0
*/
@SneakyThrows
private Expression buildCustomDeptExpression(Column deptColumn, List<Long> customDeptIds) {
if (CollectionUtil.isEmpty(customDeptIds)) {
// 没有自定义部门,返回 1=0无权限
@@ -275,13 +256,11 @@ public class MyDataPermissionHandler implements DataPermissionHandler {
return falseCondition;
}
// 构建 IN 表达式列表
ExpressionList<Expression> deptIdList = new ExpressionList<>();
for (Long deptId : customDeptIds) {
deptIdList.addExpression(new LongValue(deptId));
}
return new InExpression(deptColumn, deptIdList);
// 使用字符串解析,确保渲染始终为 IN (..)
String columnName = deptColumn.toString();
String ids = customDeptIds.stream().map(String::valueOf).reduce((a, b) -> a + "," + b).orElse("");
String sql = columnName + " IN (" + ids + ")";
return CCJSqlParserUtil.parseCondExpression(sql);
}
}

View File

@@ -20,6 +20,10 @@ import java.util.List;
@Mapper(componentModel = "spring")
public interface RoleConverter {
@Mapping(target = "dataScope", source = "dataScope")
@Mapping(target = "dataScopeLabel", expression = "java(com.youlai.boot.common.enums.DataScopeEnum.getByValue(role.getDataScope()) == null ? null : com.youlai.boot.common.enums.DataScopeEnum.getByValue(role.getDataScope()).getLabel())")
RolePageVO toPageVo(Role role);
Page<RolePageVO> toPageVo(Page<Role> page);
@Mappings({

View File

@@ -19,4 +19,12 @@ public interface UserRoleMapper extends BaseMapper<UserRole> {
* @param roleId 角色ID
*/
int countUsersByRoleId(Long roleId);
/**
* 获取角色绑定的用户ID集合
*
* @param roleId 角色ID
* @return 用户ID集合
*/
java.util.List<Long> listUserIdsByRoleId(Long roleId);
}

View File

@@ -25,11 +25,15 @@ public class RolePageVO {
@Schema(description="排序")
private Integer sort;
@Schema(description="数据权限(1-所有数据 2-部门及子部门数据 3-本部门数据 4-本人数据 5-自定义部门数据)")
private Integer dataScope;
@Schema(description="数据权限名称")
private String dataScopeLabel;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
}

View File

@@ -30,4 +30,12 @@ public interface UserRoleService extends IService<UserRole> {
* @return true已分配 false未分配
*/
boolean hasAssignedUsers(Long roleId);
/**
* 获取角色绑定的用户ID集合
*
* @param roleId 角色ID
* @return 用户ID集合
*/
List<Long> listUserIdsByRoleId(Long roleId);
}

View File

@@ -4,9 +4,11 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.boot.security.token.TokenManager;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.boot.common.constant.SystemConstants;
import com.youlai.boot.common.enums.DataScopeEnum;
import com.youlai.boot.core.exception.BusinessException;
import com.youlai.boot.security.model.RoleDataScope;
@@ -17,7 +19,6 @@ import com.youlai.boot.system.model.entity.RoleMenu;
import com.youlai.boot.system.model.form.RoleForm;
import com.youlai.boot.system.model.query.RoleQuery;
import com.youlai.boot.system.model.vo.RolePageVO;
import com.youlai.boot.common.constant.SystemConstants;
import com.youlai.boot.common.model.Option;
import com.youlai.boot.security.util.SecurityUtils;
import com.youlai.boot.system.service.RoleDeptService;
@@ -47,6 +48,7 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
private final RoleMenuService roleMenuService;
private final RoleDeptService roleDeptService;
private final UserRoleService userRoleService;
private final TokenManager tokenManager;
private final RoleConverter roleConverter;
/**
@@ -111,9 +113,14 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
// 编辑角色时,判断角色是否存在
Role oldRole = null;
List<Long> oldDeptIds = null;
if (roleId != null) {
oldRole = this.getById(roleId);
Assert.isTrue(oldRole != null, "角色不存在");
if (DataScopeEnum.CUSTOM.getValue().equals(oldRole.getDataScope())) {
oldDeptIds = roleDeptService.getDeptIdsByRoleId(roleId);
}
}
String roleCode = roleForm.getCode();
@@ -147,6 +154,25 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
)) {
roleMenuService.refreshRolePermsCache(oldRole.getCode(), roleCode);
}
// 数据权限发生变化时失效该角色关联用户的登录态JWT tokenVersion
if (oldRole != null) {
boolean dataScopeChanged = !ObjectUtil.equals(oldRole.getDataScope(), roleForm.getDataScope());
boolean customDeptChanged = false;
if (!dataScopeChanged && DataScopeEnum.CUSTOM.getValue().equals(roleForm.getDataScope())) {
List<Long> newDeptIds = roleForm.getDeptIds() != null ? roleForm.getDeptIds() : List.of();
List<Long> oldIds = oldDeptIds != null ? oldDeptIds : List.of();
customDeptChanged = !new java.util.HashSet<>(oldIds).equals(new java.util.HashSet<>(newDeptIds));
}
if (dataScopeChanged || customDeptChanged) {
List<Long> userIds = userRoleService.listUserIdsByRoleId(savedRoleId);
if (CollectionUtil.isNotEmpty(userIds)) {
userIds.forEach(tokenManager::invalidateUserSessions);
}
}
}
}
return result;
}

View File

@@ -94,4 +94,12 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
int count = this.baseMapper.countUsersByRoleId(roleId);
return count > 0;
}
@Override
public List<Long> listUserIdsByRoleId(Long roleId) {
if (roleId == null) {
return List.of();
}
return this.baseMapper.listUserIdsByRoleId(roleId);
}
}