fix: 数据权限调整后引发的问题修复
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user