refactor(extension/datapermission): 优化数据权限模块代码

- 新增 DataPermissionConstants 和 DataPermissionException 类
- 重构 DataPermissionUserDataProvider 接口位置
- 优化 RoleData 和 UserData 类,使用 Long 类型替代 String 类型
- 重构 DefaultDataPermissionHandler 类,改进数据权限处理逻辑
This commit is contained in:
2025-07-20 15:29:40 +08:00
parent 43ba770971
commit 5dd6808bea
7 changed files with 182 additions and 43 deletions

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.datapermission.constant;
/**
* 数据权限常量
*
* @author Charles7c
* @since 2.13.2
*/
public final class DataPermissionConstants {
/**
* 数据库字段:祖先节点
*/
public static final String ANCESTORS_COLUMN = "ancestors";
/**
* 方法名后缀COUNT
*/
public static final String COUNT_METHOD_SUFFIX = "_COUNT";
private DataPermissionConstants() {
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.extension.datapermission.exception;
import top.continew.starter.core.exception.BaseException;
/**
* 数据权限异常
*
* @author Charles7c
* @since 2.13.2
*/
public class DataPermissionException extends BaseException {
public DataPermissionException(String message) {
super(message);
}
public DataPermissionException(String message, Throwable cause) {
super(message, cause);
}
public static DataPermissionException unsupportedDataScope(String dataScope) {
return new DataPermissionException("Unsupported data scope: " + dataScope);
}
public static DataPermissionException unsupportedDatabase(String database) {
return new DataPermissionException("Unsupported database for data permission: " + database);
}
public static DataPermissionException invalidUserData(String message) {
return new DataPermissionException("Invalid user data: " + message);
}
public static DataPermissionException methodNotFound(String mappedStatementId) {
return new DataPermissionException("Method not found for data permission: " + mappedStatementId);
}
}

View File

@@ -29,7 +29,7 @@ public class RoleData {
/** /**
* 角色 ID * 角色 ID
*/ */
private String roleId; private Long roleId;
/** /**
* 数据权限 * 数据权限
@@ -39,16 +39,16 @@ public class RoleData {
public RoleData() { public RoleData() {
} }
public RoleData(String roleId, DataScope dataScope) { public RoleData(Long roleId, DataScope dataScope) {
this.roleId = roleId; this.roleId = roleId;
this.dataScope = dataScope; this.dataScope = dataScope;
} }
public String getRoleId() { public Long getRoleId() {
return roleId; return roleId;
} }
public void setRoleId(String roleId) { public void setRoleId(Long roleId) {
this.roleId = roleId; this.roleId = roleId;
} }

View File

@@ -16,6 +16,7 @@
package top.continew.starter.extension.datapermission.model; package top.continew.starter.extension.datapermission.model;
import java.util.Collections;
import java.util.Set; import java.util.Set;
/** /**
@@ -29,23 +30,32 @@ public class UserData {
/** /**
* 用户 ID * 用户 ID
*/ */
private String userId; private Long userId;
/** /**
* 角色列表 * 角色列表
*/ */
private Set<RoleData> roles; private Set<RoleData> roles = Collections.emptySet();
/** /**
* 部门 ID * 部门 ID
*/ */
private String deptId; private Long deptId;
public String getUserId() { public UserData() {
}
public UserData(Long userId, Long deptId, Set<RoleData> roles) {
this.userId = userId;
this.deptId = deptId;
this.roles = roles != null ? roles : Collections.emptySet();
}
public Long getUserId() {
return userId; return userId;
} }
public void setUserId(String userId) { public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }
@@ -54,14 +64,23 @@ public class UserData {
} }
public void setRoles(Set<RoleData> roles) { public void setRoles(Set<RoleData> roles) {
this.roles = roles; this.roles = roles != null ? roles : Collections.emptySet();
} }
public String getDeptId() { public Long getDeptId() {
return deptId; return deptId;
} }
public void setDeptId(String deptId) { public void setDeptId(Long deptId) {
this.deptId = deptId; this.deptId = deptId;
} }
/**
* 检查用户数据是否有效
*
* @return 是否有效
*/
public boolean isValid() {
return userId != null && deptId != null && !roles.isEmpty();
}
} }

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.starter.extension.datapermission.filter; package top.continew.starter.extension.datapermission.provider;
import top.continew.starter.extension.datapermission.model.UserData; import top.continew.starter.extension.datapermission.model.UserData;

View File

@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import top.continew.starter.core.constant.PropertiesConstants; import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserDataProvider; import top.continew.starter.extension.datapermission.provider.DataPermissionUserDataProvider;
import top.continew.starter.extension.datapermission.handler.DefaultDataPermissionHandler; import top.continew.starter.extension.datapermission.handler.DefaultDataPermissionHandler;
/** /**

View File

@@ -46,8 +46,10 @@ import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.data.enums.DatabaseType; import top.continew.starter.data.enums.DatabaseType;
import top.continew.starter.data.util.MetaUtils; import top.continew.starter.data.util.MetaUtils;
import top.continew.starter.extension.datapermission.annotation.DataPermission; import top.continew.starter.extension.datapermission.annotation.DataPermission;
import top.continew.starter.extension.datapermission.constant.DataPermissionConstants;
import top.continew.starter.extension.datapermission.enums.DataScope; import top.continew.starter.extension.datapermission.enums.DataScope;
import top.continew.starter.extension.datapermission.filter.DataPermissionUserDataProvider; import top.continew.starter.extension.datapermission.exception.DataPermissionException;
import top.continew.starter.extension.datapermission.provider.DataPermissionUserDataProvider;
import top.continew.starter.extension.datapermission.model.RoleData; import top.continew.starter.extension.datapermission.model.RoleData;
import top.continew.starter.extension.datapermission.model.UserData; import top.continew.starter.extension.datapermission.model.UserData;
@@ -62,7 +64,6 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
private static final Logger log = LoggerFactory.getLogger(DefaultDataPermissionHandler.class); private static final Logger log = LoggerFactory.getLogger(DefaultDataPermissionHandler.class);
private final DataPermissionUserDataProvider dataPermissionUserDataProvider; private final DataPermissionUserDataProvider dataPermissionUserDataProvider;
private static final DataSource dataSource = SpringUtil.getBean(DataSource.class);
public DefaultDataPermissionHandler(DataPermissionUserDataProvider dataPermissionUserDataProvider) { public DefaultDataPermissionHandler(DataPermissionUserDataProvider dataPermissionUserDataProvider) {
this.dataPermissionUserDataProvider = dataPermissionUserDataProvider; this.dataPermissionUserDataProvider = dataPermissionUserDataProvider;
@@ -71,26 +72,47 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
@Override @Override
public Expression getSqlSegment(Expression where, String mappedStatementId) { public Expression getSqlSegment(Expression where, String mappedStatementId) {
try { try {
Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId DataPermission dataPermission = findDataPermissionAnnotation(mappedStatementId);
.lastIndexOf(StringConstants.DOT))); if (dataPermission != null && dataPermissionUserDataProvider.isFilter()) {
String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(StringConstants.DOT) + 1);
Method[] methodArr = clazz.getMethods();
for (Method method : methodArr) {
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
String name = method.getName();
if (dataPermission == null || !CharSequenceUtil.equalsAny(methodName, name, name + "_COUNT")) {
continue;
}
if (dataPermissionUserDataProvider.isFilter()) {
return buildDataScopeFilter(dataPermission, where); return buildDataScopeFilter(dataPermission, where);
} }
} } catch (Exception e) {
} catch (ClassNotFoundException e) {
log.error("Data permission handler build data scope filter occurred an error: {}.", e.getMessage(), e); log.error("Data permission handler build data scope filter occurred an error: {}.", e.getMessage(), e);
} }
return where; return where;
} }
/**
* 查找数据权限注解
*
* @param mappedStatementId Mapper 方法 ID
* @return 数据权限注解
*/
private DataPermission findDataPermissionAnnotation(String mappedStatementId) {
try {
int lastDotIndex = mappedStatementId.lastIndexOf(StringConstants.DOT);
if (lastDotIndex == -1) {
return null;
}
String className = mappedStatementId.substring(0, lastDotIndex);
String methodName = mappedStatementId.substring(lastDotIndex + 1);
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String name = method.getName();
if (CharSequenceUtil.equalsAny(methodName, name, name + DataPermissionConstants.COUNT_METHOD_SUFFIX)) {
return method.getAnnotation(DataPermission.class);
}
}
} catch (ClassNotFoundException e) {
throw DataPermissionException.methodNotFound(mappedStatementId);
}
return null;
}
/** /**
* 构建数据范围过滤条件 * 构建数据范围过滤条件
* *
@@ -99,23 +121,29 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
* @return 构建后查询条件 * @return 构建后查询条件
*/ */
private Expression buildDataScopeFilter(DataPermission dataPermission, Expression where) { private Expression buildDataScopeFilter(DataPermission dataPermission, Expression where) {
Expression expression = null;
UserData userData = dataPermissionUserDataProvider.getUserData(); UserData userData = dataPermissionUserDataProvider.getUserData();
if (userData == null || !userData.isValid()) {
throw DataPermissionException.invalidUserData("User data is null or invalid");
}
Expression expression = null;
Set<RoleData> roles = userData.getRoles(); Set<RoleData> roles = userData.getRoles();
for (RoleData roleData : roles) { for (RoleData roleData : roles) {
DataScope dataScope = roleData.getDataScope(); DataScope dataScope = roleData.getDataScope();
if (DataScope.ALL.equals(dataScope)) { if (DataScope.ALL.equals(dataScope)) {
return where; return where;
} }
switch (dataScope) {
case DEPT_AND_CHILD -> expression = this expression = switch (dataScope) {
.buildDeptAndChildExpression(dataPermission, userData, expression); case DEPT_AND_CHILD -> buildDeptAndChildExpression(dataPermission, userData, expression);
case DEPT -> expression = this.buildDeptExpression(dataPermission, userData, expression); case DEPT -> buildDeptExpression(dataPermission, userData, expression);
case SELF -> expression = this.buildSelfExpression(dataPermission, userData, expression); case SELF -> buildSelfExpression(dataPermission, userData, expression);
case CUSTOM -> expression = this.buildCustomExpression(dataPermission, roleData, expression); case CUSTOM -> buildCustomExpression(dataPermission, roleData, expression);
default -> throw new IllegalArgumentException("暂不支持 [%s] 数据权限".formatted(dataScope)); default -> throw DataPermissionException.unsupportedDataScope(dataScope.toString());
} };
} }
return where != null ? new AndExpression(where, new ParenthesedExpressionList<>(expression)) : expression; return where != null ? new AndExpression(where, new ParenthesedExpressionList<>(expression)) : expression;
} }
@@ -144,18 +172,19 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
equalsTo.setLeftExpression(new Column(dataPermission.id())); equalsTo.setLeftExpression(new Column(dataPermission.id()));
equalsTo.setRightExpression(new LongValue(userData.getDeptId())); equalsTo.setRightExpression(new LongValue(userData.getDeptId()));
DatabaseType databaseType = MetaUtils.getDatabaseType(dataSource); DatabaseType databaseType = MetaUtils.getDatabaseType(SpringUtil.getBean(DataSource.class));
Expression inSetExpression; Expression inSetExpression;
if (DatabaseType.MYSQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) { if (DatabaseType.MYSQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
Function findInSetFunction = new Function(); Function findInSetFunction = new Function();
findInSetFunction.setName("find_in_set"); findInSetFunction.setName("find_in_set");
findInSetFunction.setParameters(new ExpressionList(new LongValue(userData findInSetFunction.setParameters(new ExpressionList(new LongValue(userData
.getDeptId()), new Column("ancestors"))); .getDeptId()), new Column(DataPermissionConstants.ANCESTORS_COLUMN)));
inSetExpression = findInSetFunction; inSetExpression = findInSetFunction;
} else if (DatabaseType.POSTGRE_SQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) { } else if (DatabaseType.POSTGRE_SQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
// 构建 concat 函数 // 构建 concat 函数
Function concatFunction = new Function("concat"); Function concatFunction = new Function("concat");
concatFunction.setParameters(new ExpressionList<>(new Column("ancestors"), new StringValue(","))); concatFunction
.setParameters(new ExpressionList<>(new Column(DataPermissionConstants.ANCESTORS_COLUMN), new StringValue(",")));
// 创建 LIKE 函数 // 创建 LIKE 函数
LikeExpression likeExpression = new LikeExpression(); LikeExpression likeExpression = new LikeExpression();
@@ -163,7 +192,7 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
likeExpression.setRightExpression(new StringValue("%," + userData.getDeptId() + ",%")); likeExpression.setRightExpression(new StringValue("%," + userData.getDeptId() + ",%"));
inSetExpression = likeExpression; inSetExpression = likeExpression;
} else { } else {
throw new IllegalArgumentException("暂不支持 [%s] 数据权限".formatted("")); throw DataPermissionException.unsupportedDatabase(databaseType.getDatabase());
} }
select.setWhere(new OrExpression(equalsTo, inSetExpression)); select.setWhere(new OrExpression(equalsTo, inSetExpression));