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
*/
private String roleId;
private Long roleId;
/**
* 数据权限
@@ -39,16 +39,16 @@ public class RoleData {
public RoleData() {
}
public RoleData(String roleId, DataScope dataScope) {
public RoleData(Long roleId, DataScope dataScope) {
this.roleId = roleId;
this.dataScope = dataScope;
}
public String getRoleId() {
public Long getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
public void setRoleId(Long roleId) {
this.roleId = roleId;
}

View File

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

View File

@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.core.ResolvableType;
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;
/**

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.util.MetaUtils;
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.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.UserData;
@@ -62,7 +64,6 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
private static final Logger log = LoggerFactory.getLogger(DefaultDataPermissionHandler.class);
private final DataPermissionUserDataProvider dataPermissionUserDataProvider;
private static final DataSource dataSource = SpringUtil.getBean(DataSource.class);
public DefaultDataPermissionHandler(DataPermissionUserDataProvider dataPermissionUserDataProvider) {
this.dataPermissionUserDataProvider = dataPermissionUserDataProvider;
@@ -71,26 +72,47 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
@Override
public Expression getSqlSegment(Expression where, String mappedStatementId) {
try {
Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId
.lastIndexOf(StringConstants.DOT)));
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);
}
DataPermission dataPermission = findDataPermissionAnnotation(mappedStatementId);
if (dataPermission != null && dataPermissionUserDataProvider.isFilter()) {
return buildDataScopeFilter(dataPermission, where);
}
} catch (ClassNotFoundException e) {
} catch (Exception e) {
log.error("Data permission handler build data scope filter occurred an error: {}.", e.getMessage(), e);
}
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 构建后查询条件
*/
private Expression buildDataScopeFilter(DataPermission dataPermission, Expression where) {
Expression expression = null;
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();
for (RoleData roleData : roles) {
DataScope dataScope = roleData.getDataScope();
if (DataScope.ALL.equals(dataScope)) {
return where;
}
switch (dataScope) {
case DEPT_AND_CHILD -> expression = this
.buildDeptAndChildExpression(dataPermission, userData, expression);
case DEPT -> expression = this.buildDeptExpression(dataPermission, userData, expression);
case SELF -> expression = this.buildSelfExpression(dataPermission, userData, expression);
case CUSTOM -> expression = this.buildCustomExpression(dataPermission, roleData, expression);
default -> throw new IllegalArgumentException("暂不支持 [%s] 数据权限".formatted(dataScope));
}
expression = switch (dataScope) {
case DEPT_AND_CHILD -> buildDeptAndChildExpression(dataPermission, userData, expression);
case DEPT -> buildDeptExpression(dataPermission, userData, expression);
case SELF -> buildSelfExpression(dataPermission, userData, expression);
case CUSTOM -> buildCustomExpression(dataPermission, roleData, expression);
default -> throw DataPermissionException.unsupportedDataScope(dataScope.toString());
};
}
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.setRightExpression(new LongValue(userData.getDeptId()));
DatabaseType databaseType = MetaUtils.getDatabaseType(dataSource);
DatabaseType databaseType = MetaUtils.getDatabaseType(SpringUtil.getBean(DataSource.class));
Expression inSetExpression;
if (DatabaseType.MYSQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
Function findInSetFunction = new Function();
findInSetFunction.setName("find_in_set");
findInSetFunction.setParameters(new ExpressionList(new LongValue(userData
.getDeptId()), new Column("ancestors")));
.getDeptId()), new Column(DataPermissionConstants.ANCESTORS_COLUMN)));
inSetExpression = findInSetFunction;
} else if (DatabaseType.POSTGRE_SQL.getDatabase().equalsIgnoreCase(databaseType.getDatabase())) {
// 构建 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 函数
LikeExpression likeExpression = new LikeExpression();
@@ -163,7 +192,7 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
likeExpression.setRightExpression(new StringValue("%," + userData.getDeptId() + ",%"));
inSetExpression = likeExpression;
} else {
throw new IllegalArgumentException("暂不支持 [%s] 数据权限".formatted(""));
throw DataPermissionException.unsupportedDatabase(databaseType.getDatabase());
}
select.setWhere(new OrExpression(equalsTo, inSetExpression));