Spring Boot + MyBatis-Plus 數據權限管理入門技術方案
作者:farerboy
通過注解驅動的方式實現靈活的數據權限控制,可根據實際業務需求擴展權限類型和過濾規則。建議結合具體業務場景調整數據權限模型和SQL生成策略。?
一、需求分析
實現不同用戶/角色在訪問同一數據接口時,根據預設規則動態過濾數據。例如:
- 普通用戶只能查看自己創建的數據
- 部門管理員可查看本部門所有數據
- 超級管理員可查看全部數據
二、技術選型
- Spring Boot 3.x:基礎框架
- MyBatis-Plus 3.5+:數據訪問層增強
- Sa-Token/Spring Security:權限認證(可選)
- Jackson:JSON處理
- MySQL:數據庫
三、核心設計
3.1 數據權限模型
@Data
public class DataScope {
// 權限類型:ALL, DEPT, SELF, CUSTOM
private String scopeType;
// 可見部門ID集合
private Set<Long> deptIds;
// 用戶ID
private Long userId;
// 自定義SQL條件
private String customCondition;
}
3.2 實現方案
通過MyBatis-Plus的攔截器機制動態修改SQL,結合自定義注解實現聲明式數據權限控制。
圖片
四、實現步驟
4.1 添加依賴(pom.xml)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
4.2 定義數據權限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {
/**
* 部門表別名(多表關聯時使用)
*/
String deptAlias() default "";
/**
* 用戶ID字段名(默認create_by)
*/
String userColumn() default "create_by";
/**
* 部門ID字段名(默認dept_id)
*/
String deptColumn() default "dept_id";
}
4.3 實現數據權限攔截器
@Intercepts({
@Signature(type = Executor.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DataPermissionInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 獲取當前用戶數據權限
DataScope dataScope = SecurityUtils.getDataScope();
if (dataScope == null || dataScope.isAllAccess()) {
return invocation.proceed();
}
StatementHandler handler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(handler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// 獲取注解信息
DataPermission dataPermission = getDataPermissionAnnotation(mappedStatement);
if (dataPermission != null) {
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
String newSql = buildDataScopeSql(originalSql, dataScope, dataPermission);
metaObject.setValue("delegate.boundSql.sql", newSql);
}
return invocation.proceed();
}
private String buildDataScopeSql(String originalSql, DataScope dataScope, DataPermission annotation) {
StringBuilder where = new StringBuilder();
switch (dataScope.getScopeType()) {
case "DEPT":
where.append(annotation.deptColumn())
.append(" IN (")
.append(StringUtils.join(dataScope.getDeptIds(), ","))
.append(")");
break;
case "SELF":
where.append(annotation.userColumn())
.append(" = ")
.append(dataScope.getUserId());
break;
case "CUSTOM":
where.append(dataScope.getCustomCondition());
break;
}
if (originalSql.toUpperCase().contains("WHERE")) {
return originalSql + " AND " + where;
} else {
return originalSql + " WHERE " + where;
}
}
}
4.4 注冊攔截器
@Configuration
public class MybatisPlusConfig {
@Bean
public DataPermissionInterceptor dataPermissionInterceptor() {
return new DataPermissionInterceptor();
}
}
4.5 Service層應用
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> {
@DataPermission(deptColumn = "order_dept_id")
public Page<Order> listOrders(Page<Order> page) {
return baseMapper.selectPage(page, null);
}
}
五、初步數據表設計
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY,
username VARCHAR(50),
dept_id BIGINT
);
CREATE TABLE sys_order (
id BIGINT PRIMARY KEY,
order_no VARCHAR(50),
create_by BIGINT COMMENT '創建人ID',
order_dept_id BIGINT COMMENT '訂單所屬部門'
);
六、擴展優化
- 多表關聯處理:通過注解的deptAlias指定表別名
- 緩存優化:緩存用戶權限數據,避免頻繁查詢
- 租戶隔離:結合多租戶方案實現更復雜場景
- 性能監控:記錄SQL修改日志用于審計
七、測試驗證
@Test
void testDataPermission() {
// 模擬普通用戶登錄
loginUser("user1", "DEPT", Set.of(1001L));
List<Order> orders = orderService.listOrders();
assertThat(orders).allMatch(order -> order.getOrderDeptId() == 1001);
// 模擬管理員登錄
loginUser("admin", "ALL", null);
orders = orderService.listOrders();
assertThat(orders).hasSize(100);
}
八、注意事項
- SQL注入防護:嚴格校驗自定義條件表達式
- 索引優化:確保添加的過濾條件字段有合適索引
- 事務處理:在事務邊界內保持數據權限一致性
- 性能影響:避免過度復雜的權限條件影響查詢性能
該方案通過注解驅動的方式實現靈活的數據權限控制,可根據實際業務需求擴展權限類型和過濾規則。建議結合具體業務場景調整數據權限模型和SQL生成策略。
責任編輯:武曉燕
來源:
小編程聊林