本文目录导读:

我来为你详细介绍Java数据权限的开发方案,包括完整的案例实现。
数据权限常见实现方案
方案对比
- 硬编码:在Service层写死权限逻辑
- 注解+AOP:通过注解标记,AOP拦截处理
- 数据权限框架:如MyBatis-Plus的DataPermissionInterceptor
- 规则引擎:动态配置权限规则
推荐方案:注解+AOP + MyBatis-Plus拦截器
完整案例实现
项目结构
src/main/java/com/example/datapermission/
├── annotation/
│ └── DataPermission.java
├── config/
│ └── DataPermissionInterceptor.java
├── entity/
│ ├── User.java
│ └── Order.java
├── mapper/
│ └── OrderMapper.java
├── service/
│ └── OrderService.java
└── controller/
└── OrderController.java
核心代码实现
数据权限注解
package com.example.datapermission.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
/**
* 数据权限类型
*/
PermissionType type() default PermissionType.ALL;
/**
* 表别名
*/
String tableAlias() default "";
/**
* 用户ID字段名
*/
String userIdField() default "user_id";
/**
* 部门ID字段名
*/
String deptIdField() default "dept_id";
}
enum PermissionType {
ALL, // 全部数据
DEPT, // 本部门数据
DEPT_DOWN, // 本部门及下级部门
SELF // 个人数据
}
自定义拦截器
package com.example.datapermission.config;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.example.datapermission.annotation.DataPermission;
import com.example.datapermission.annotation.PermissionType;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;
@Slf4j
@Component
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DataPermissionInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// 获取方法上的注解
DataPermission dataPermission = getDataPermission(mappedStatement);
if (dataPermission == null) {
return invocation.proceed();
}
// 解析SQL
String sql = statementHandler.getBoundSql().getSql();
try {
String newSql = buildPermissionSql(sql, dataPermission);
// 替换原始SQL
PluginUtils.MPParameterHandler handler = (PluginUtils.MPParameterHandler) metaObject.getValue("delegate.parameterHandler");
metaObject.setValue("delegate.boundSql.sql", newSql);
} catch (JSQLParserException e) {
log.error("SQL解析失败", e);
}
return invocation.proceed();
}
/**
* 构建权限SQL
*/
private String buildPermissionSql(String sql, DataPermission permission) throws JSQLParserException {
Select select = (Select) CCJSqlParserUtil.parse(sql);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
// 根据权限类型添加条件
String condition = getPermissionCondition(permission);
if (condition != null && !condition.isEmpty()) {
Expression where = plainSelect.getWhere();
Expression newCondition = CCJSqlParserUtil.parseCondExpression(condition);
if (where == null) {
plainSelect.setWhere(newCondition);
} else {
plainSelect.setWhere(new AndExpression(where, newCondition));
}
}
return select.toString();
}
/**
* 获取权限条件
*/
private String getPermissionCondition(DataPermission permission) {
// 获取当前用户信息(从SecurityContext或其他方式)
CurrentUser user = SecurityUtils.getCurrentUser();
if (user == null) {
return null;
}
String tableAlias = permission.tableAlias();
String alias = tableAlias.isEmpty() ? "" : tableAlias + ".";
switch (permission.type()) {
case ALL:
return null; // 全部数据,不加条件
case DEPT:
return String.format("%s%s = %d", alias, permission.deptIdField(), user.getDeptId());
case DEPT_DOWN:
// 递归查询部门及下级部门
List<Long> deptIds = deptService.getDeptAndChildrenIds(user.getDeptId());
String deptIdStr = deptIds.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
return String.format("%s%s IN (%s)", alias, permission.deptIdField(), deptIdStr);
case SELF:
return String.format("%s%s = %d", alias, permission.userIdField(), user.getUserId());
default:
return null;
}
}
/**
* 获取方法上的数据权限注解
*/
private DataPermission getDataPermission(MappedStatement mappedStatement) {
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
try {
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
return method.getAnnotation(DataPermission.class);
}
}
} catch (ClassNotFoundException e) {
log.error("Class not found: {}", className);
}
return null;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
用户工具类
package com.example.datapermission.config;
public class SecurityUtils {
/**
* 获取当前登录用户
*/
public static CurrentUser getCurrentUser() {
// 从SecurityContext或Session中获取用户信息
// 这里简单模拟
return new CurrentUser(1L, "admin", 1L);
}
@Data
@AllArgsConstructor
public static class CurrentUser {
private Long userId;
private String username;
private Long deptId;
}
}
实体类
package com.example.datapermission.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("t_order")
public class Order {
@TableId
private Long id;
private String orderNo;
private Long userId;
private Long deptId;
private BigDecimal amount;
private LocalDateTime createTime;
}
Mapper接口
package com.example.datapermission.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.datapermission.annotation.DataPermission;
import com.example.datapermission.annotation.PermissionType;
import com.example.datapermission.entity.Order;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface OrderMapper extends BaseMapper<Order> {
/**
* 查询个人订单
*/
@DataPermission(type = PermissionType.SELF, tableAlias = "o")
@Select("SELECT * FROM t_order o WHERE o.status = 1")
List<Order> getMyOrders();
/**
* 查询部门订单
*/
@DataPermission(type = PermissionType.DEPT, tableAlias = "o")
@Select("SELECT * FROM t_order o WHERE o.status = 1")
List<Order> getDeptOrders();
/**
* 查询所有订单(管理员)
*/
@DataPermission(type = PermissionType.ALL)
@Select("SELECT * FROM t_order")
List<Order> getAllOrders();
}
Service实现
package com.example.datapermission.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.datapermission.entity.Order;
import com.example.datapermission.mapper.OrderMapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderService extends ServiceImpl<OrderMapper, Order> {
/**
* 获取我的订单
*/
public List<Order> getMyOrders() {
return baseMapper.getMyOrders();
}
/**
* 获取部门订单
*/
public List<Order> getDeptOrders() {
return baseMapper.getDeptOrders();
}
/**
* 获取所有订单
*/
public List<Order> getAllOrders() {
return baseMapper.getAllOrders();
}
}
Controller
package com.example.datapermission.controller;
import com.example.datapermission.entity.Order;
import com.example.datapermission.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/my")
public List<Order> getMyOrders() {
return orderService.getMyOrders();
}
@GetMapping("/dept")
public List<Order> getDeptOrders() {
return orderService.getDeptOrders();
}
@GetMapping("/all")
public List<Order> getAllOrders() {
return orderService.getAllOrders();
}
}
配置文件
<!-- application.yml -->
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
高级扩展
MyBatis-Plus自带拦截器
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加数据权限拦截器
interceptor.addInnerInterceptor(new DataPermissionInterceptorPlus());
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// 使用MyBatis-Plus的DataPermissionInterceptor
public class DataPermissionInterceptorPlus implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) {
// 获取数据权限
DataPermission permission = getDataPermission(ms);
if (permission == null) {
return;
}
// 构建权限条件
String condition = buildPermissionCondition(permission);
if (condition != null) {
// 通过PluginUtils添加条件
PluginUtils.addDataPermissionCondition(condition);
}
}
}
动态权限规则
@Service
public class DynamicPermissionService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 获取用户的数据权限规则
*/
public PermissionRule getPermissionRule(Long userId) {
String key = "permission:rule:" + userId;
PermissionRule rule = (PermissionRule) redisTemplate.opsForValue().get(key);
if (rule == null) {
// 从数据库加载
rule = loadFromDatabase(userId);
redisTemplate.opsForValue().set(key, rule, 1, TimeUnit.HOURS);
}
return rule;
}
@Data
public static class PermissionRule {
private PermissionType type;
private List<Long> allowedDeptIds;
private List<String> allowedTables;
private Map<String, Object> customConditions;
}
}
最佳实践建议
性能优化
- 缓存用户权限信息
- 使用索引优化查询
- 避免复杂的子查询
安全考虑
- 防止SQL注入
- 权限规则的动态校验
- 敏感数据的脱敏处理
可维护性
- 统一权限配置管理
- 完善的日志记录
- 权限规则的版本控制
这个案例提供了一个完整的数据权限解决方案,你可以根据实际业务需求进行调整和扩展。