Java数据权限案例如何开发

wen java案例 26

本文目录导读:

Java数据权限案例如何开发

  1. 数据权限常见实现方案
  2. 完整案例实现
  3. 高级扩展
  4. 最佳实践建议

我来为你详细介绍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注入
  • 权限规则的动态校验
  • 敏感数据的脱敏处理

可维护性

  • 统一权限配置管理
  • 完善的日志记录
  • 权限规则的版本控制

这个案例提供了一个完整的数据权限解决方案,你可以根据实际业务需求进行调整和扩展。

抱歉,评论功能暂时关闭!