本文目录导读:

我来为你详细介绍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注入
- 权限规则的动态校验
- 敏感数据的脱敏处理
可维护性
- 统一权限配置管理
- 完善的日志记录
- 权限规则的版本控制
这个案例提供了一个完整的数据权限解决方案,你可以根据实际业务需求进行调整和扩展。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播