mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-13 22:58:39 +08:00
refactor(extension/datapermission): 优化数据权限模块代码
- 新增 DataPermissionConstants 和 DataPermissionException 类 - 重构 DataPermissionUserDataProvider 接口位置 - 优化 RoleData 和 UserData 类,使用 Long 类型替代 String 类型 - 重构 DefaultDataPermissionHandler 类,改进数据权限处理逻辑
This commit is contained in:
@@ -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() {
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
|
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -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));
|
||||||
|
Reference in New Issue
Block a user