当前位置: 首页 > news >正文

java数据权限过滤

java数据权限过滤

  • 使用切面
  • 使用mybatis拦截器

使用切面

  1. 数据权限过滤注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataFilter {String FILTER_TYPE_EQ = "=";String FILTER_TYPE_IN = "IN";/*** 表别名*/String alias() default "";/*** 过滤字段*/String field() default "";/*** 过滤类型*/String type() default FILTER_TYPE_IN;
}
  1. 基础查询类:想要使用数据过滤的方法,其参数需要继承该类。
@Data
public class Query {/*** 查询条件子句*/@JsonIgnoreprivate String clause;/*** 查询条件参数*/@JsonIgnoreprivate String filterParams;
}
  1. 切面类
@Aspect
@Component
public class DataFilterAspect {private final Logger log = LoggerFactory.getLogger(DataFilterAspect.class);@Before("@annotation(dataFilter)")public void doBefore(JoinPoint point, DataFilter dataFilter) {// 1.获取调用的方法参数Object arg = point.getArgs()[0];if (!(arg instanceof Query)) {log.warn("调用方法[{}], 参数不是Query对象", point.getSignature().getName());return;}Query query = (Query) arg;// 获取当前的用户LoginUser loginUser = SecurityUtils.getLoginUser();SysUser user = loginUser.getUser();if (user.isAdmin()) {// 如果是超级管理员,则不过滤数据query.setClause(null);return;}// 2.获取数据过滤注解的参数String alias = dataFilter.alias();String field = dataFilter.field();String type = dataFilter.type();if (StringUtils.isEmpty(field)) {log.warn("调用方法[{}], 数据过滤注解的field参数为空", point.getSignature().getName());return;}if (StringUtils.isEmpty(type)) {log.warn("调用方法[{}], 数据过滤注解的type参数为空", point.getSignature().getName());return;}// 3.设置查询sql子句if (StringUtils.isNotEmpty(query.getClause())) {return;}String clause;if (StringUtils.isNotEmpty(query.getFilterParams())) {if (StringUtils.isNotEmpty(alias)) {clause = StringUtils.format("{}.{} {} {}", alias, field, type, query.getFilterParams());} else {clause = StringUtils.format("{} {} {}", field, type, query.getFilterParams());}query.setClause(clause);return;}if (StringUtils.isNotEmpty(alias)) {clause = StringUtils.format("{}.{} IN ( SELECT company_code FROM sys_user_company WHERE user_id = {} )", alias, field, user.getUserId());} else {clause = StringUtils.format("{} IN ( SELECT company_code FROM sys_user_company WHERE user_id = {} )", field, user.getUserId());}query.setClause(clause);}
}
  1. 使用

    1. mapper.xml 里的sql需要加入如下内容:
    <if test="query.clause != null and query.clause != ''">AND ${query.clause}
    </if>
    
    1. mapper类方法上加上注解:@DataFilter(alias = "t1", field = "company_code")

使用mybatis拦截器

  1. 工具类
public class DataFilter {/*** sql条件子句*/protected static final ThreadLocal<String> LOCAL_CLAUSE = new ThreadLocal<>();protected static void setClause(String clause) {LOCAL_CLAUSE.set(clause);}public static String getClause() {return LOCAL_CLAUSE.get();}public static void clear() {LOCAL_CLAUSE.remove();}/*** 用指定的sql子句进行数据过滤* <p>使用此方法需要被执行的sql中带有 WHERE 子句,例如:WHERE 1 = 1</p>* @param clause sql子句*/public static void startFilter(String clause) {setClause(clause);}
}
  1. 拦截器
@Intercepts({// 拦截Executor的query方法(不带CacheKey和BoundSql参数)@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),// 拦截Executor的query方法(带CacheKey和BoundSql参数)@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),// 拦截Executor的update方法(包括update、insert、delete语句)方法@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
public class DataFilterInterceptor implements Interceptor {public DataFilterInterceptor() {log.info("数据过滤拦截器初始化成功");}private static final Logger log = LoggerFactory.getLogger(DataFilterInterceptor.class);/*** 拦截方法,在SQL执行前进行处理*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取数据过滤条件String clause = DataFilter.getClause();// 如果没有过滤条件,直接执行原SQLif (clause == null) {return invocation.proceed();}log.info("数据过滤拦截器-附加sql子句:{}", clause);// 1.获取原始sqlObject[] args = invocation.getArgs();MappedStatement ms = (MappedStatement) args[0];BoundSql boundSql;// 根据参数数量判断是哪种query方法if (args.length == 6) {// 带CacheKey和BoundSql的query方法boundSql = (BoundSql) args[5];} else {// 普通query或update方法boundSql = ms.getBoundSql(args[1]);}String originalSql = boundSql.getSql();// 2.改写sql,添加数据过滤条件String newSql = appendDataFilter(originalSql, clause);// 3.替换SQLBoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,boundSql.getParameterMappings(), boundSql.getParameterObject());if (args.length == 6) {// 更新带CacheKey和BoundSql参数的query方法中的BoundSqlargs[5] = newBoundSql;} else {// 替换普通query或update方法中的MappedStatementMappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));args[0] = newMs;}// 4.执行方法return invocation.proceed();// 注意:清理ThreadLocal数据不能放在这,因为使用分页拦截器时会执行count和select两个语句,// 清除数据后会导致只有count语句会被加上过滤条件}/*** 在原始SQL中添加数据过滤条件*/private String appendDataFilter(String originalSql, String clause) {String lowerSql = originalSql.toLowerCase();// 查找order by 和 limit 的位置int orderByIndex = lowerSql.lastIndexOf("order by");int limitIndex = lowerSql.lastIndexOf("limit");if (orderByIndex != -1) {// 存在order by,将条件插入到 order by前String beforeOrderBy = originalSql.substring(0, orderByIndex);String afterOrderBy = originalSql.substring(orderByIndex);return beforeOrderBy + " AND" + clause + " " + afterOrderBy;} else {// 不存在order by,存在limit,将条件插入到 limit 前if (limitIndex != -1) {String beforeLimit = originalSql.substring(0, limitIndex);String afterLimit = originalSql.substring(limitIndex);return beforeLimit + " AND" + clause + " " + afterLimit;}// 不存在order by,不存在limit,将条件插入到sql的末尾return originalSql + " AND" + clause;}}/*** 复制 MappedStatement 并替换 BoundSql*/private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());// 复制其他属性(如 resultMaps、parameterMap 等)builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator());builder.timeout(ms.getTimeout());builder.parameterMap(ms.getParameterMap());builder.resultMaps(ms.getResultMaps());builder.resultSetType(ms.getResultSetType());builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();}/*** 自定义 SqlSource,用于动态 SQL*/public static class BoundSqlSqlSource implements SqlSource {private final BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return boundSql;}}
}
  1. 注册拦截器
@Configuration
public class MyBatisConfig {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setPlugins(new DataFilterInterceptor());return sessionFactory.getObject();}
}// mp中
@Configuration
public class MyBatisPlusConfig {@Beanpublic DataFilterInterceptor dataFilterInterceptor() {return new DataFilterInterceptor();}
}
  1. 使用
    示例:
@GetMapping("/list")
public R<List<HotelOrderVo>> list(HotelOrderReqDto dto) {List<String> codeList = sysDeptService.selectCodeList(getDeptId()).stream().map(code -> "'" + code + "'").collect(Collectors.toList());DataFilter.startFilter(" dept_id IN (" + StringUtils.join(codeList, ",") + ")");//PageUtils.startPage();List<HotelOrderVo> list = hotelOrderService.list(dto);DataFilter.clear();// 使用了分页拦截器后返回的list实际是继承了List的Page对象return R.ok(list);
}

相关过滤条件也可以直接写死在拦截器里。

http://www.dtcms.com/a/439045.html

相关文章:

  • 珠宝网站开发目的网站建设营销型号的区别
  • 网站建设方案书是什么意思wordpress最新官方默认主题
  • SPEA:强度帕累托进化算法
  • 沐风老师3DMAX快速地形插件QuickTerrain使用方法详解
  • 北京保障房建设网站图像处理专业网站
  • 丹东市住房和城乡建设网站通过手机建设网站
  • Linux 动静态库与加载原理
  • 东莞建外贸企业网站做网站需不需要购买服务器
  • 使用burp工具的intruder模块进行密码爆破
  • wordpress邮件设置广州网站优化效果
  • 做网站关键字网站建设培训心得体会
  • 清远专业网站建设服务百度如何建网站群
  • 能够做一镜到底的网站seo分析报告怎么写
  • LangChain源码分析(二)- Message系统
  • 做网站的公司推荐一 网站建设的总体目标
  • 建站资源深圳十大平面设计公司
  • 中文 网站模板网站设计模板 优帮云
  • 【Svelte】如何使用 SvelteKit load 函数中的 depends 功能?例子演示
  • 开源程序做网站任务广州专业网站建设网页设计服务
  • [linux] 用户空间高实时性响应GIC中断的完整实现讨论
  • 做窗帘店的网站十堰建设局网站
  • Xmind 2025最新安装使用教程
  • 做网站asp用什么软件wordpress页面内容显示默认
  • 开发者指南:解析Vibes架构与AI提示词,探索二次开发可能性
  • 中卫市网站开发制作如何制作网页内容
  • 【测试】商城系统---测试报告
  • 集团网站设计开发排名优化seo
  • 智能体互联系列国标草案的理解(1):总体架构设计
  • 网站网络架构申请做网站
  • Effective Modern C++ 条款31:避免使用默认捕获模式