java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Mybatis 权限

Mybatis自定义拦截器实现权限功能

作者:喔喔咿哈哈

本文主要介绍了Mybatis自定义拦截器实现权限功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1、Mybatis 拦截器介绍

1.1 Mybatis 执行流程

1.2 Mybatis中可以被拦截的类型

1.3 使用规则

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
    /**
     * 定义拦截点
     * 只有符合拦截点的条件才会进入到拦截器
     */
    Signature[] value();
}

Signature来指定咱们需要拦截那个类对象的哪个方法

- type:上述四种类型中的一种;
- method:对应接口中的哪类方法(因为可能存在重载方法);
- args:对应哪一个方法的入参;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
  /**
   * 定义拦截的类 Executor、ParameterHandler、StatementHandler、ResultSetHandler当中的一个
   */
  Class<?> type();

  /**
   * 在定义拦截类的基础之上,在定义拦截的方法
   */
  String method();

  /**
   * 在定义拦截方法的基础之上在定义拦截的方法对应的参数,
   * JAVA里面方法可能重载,故注意参数的类型和顺序
   */
  Class<?>[] args();
}

1.4 拦截器重写的方法

public interface Interceptor {
    //起拦截作用,在此定义一些功能
    Object intercept(Invocation var1) throws Throwable;

    //这个方法的作用是就是让mybatis判断,是否要进行拦截,然后做出决定是否生成一个代理
    Object plugin(Object var1);

    //拦截器需要一些变量对象,而且这个对象是支持可配置的。
    void setProperties(Properties var1);
}

2、实战部分:拦截实现

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DepartAuth {
    /**
     * 添加查询条件的字段名
     * @return
     */
    String field();

    EnumDepartAuthType authType() default EnumDepartAuthType.DEPART_ID;
    
}

在所在接口上添加注解

@DepartAuth(field = "xxx", authType = )

切面(AuthAspect)

@Slf4j
@Aspect
@Component
public class DepartAuthAspect {

    @Autowired
    private DepartAuthHandler departAuthHandler;

    @Pointcut("@annotation(org.jeecg.common.auth.depart.annotation.DepartAuth)")
    public void departAuthPoint() {

    }

    @Before("departAuthPoint()")
    public void before(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        Annotation[] annotations = methodSignature.getMethod().getAnnotations();

        for (Annotation annotation : annotations) {
            if (annotation instanceof DepartAuth) {
                String field = ((DepartAuth) annotation).field();
                departAuthHandler.beforeHandler((DepartAuth) annotation);
            }
        }

    }

    @After("departAuthPoint()")
    public void after(JoinPoint pjp) {
        departAuthHandler.afterHandler();
    }
}

DepartAuthHandler

将信息存储到 TreadLocalhost 中,方便后续修改sql的时候可以读取到需要修改的字段

@Slf4j
@Component
public class DepartAuthHandler {

    public static ThreadLocal<DepartAuth> DEPART_AUTH_CACHE = new ThreadLocal<>();
    public static ThreadLocal<Integer> DEPART_AUTH_COUNT = new ThreadLocal<>();

    public void beforeHandler(DepartAuth departAuth) {
        String field = departAuth.field();
        if(StringUtils.isNotBlank(field)) {
            DEPART_AUTH_CACHE.set(departAuth);
            DEPART_AUTH_COUNT.remove();
        }
        PriorityQueue queue = new PriorityQueue<>();
        queue.peek();
    }

    public void afterHandler() {
        DEPART_AUTH_CACHE.remove();
        DEPART_AUTH_COUNT.remove();
    }
}

拦截器部分

@Data
@Slf4j
@Component
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DepartAuthMapperInterceptor implements Interceptor {

    private Properties properties;




    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        DepartAuth departAuth = DepartAuthHandler.DEPART_AUTH_CACHE.get();
        Integer count = DepartAuthHandler.DEPART_AUTH_COUNT.get();
        if(departAuth != null && count == null) {
            // 说明当前线程已经执行了过滤条件,避免递归调用
            DepartAuthHandler.DEPART_AUTH_COUNT.set(1);
            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
            //获取StatementHandler构造器
            StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
            // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
            MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
            SqlCommandType commandType = mappedStatement.getSqlCommandType();
            // 处理select对象
            if (SqlCommandType.SELECT.equals(commandType)) {
                // 获取原始sql
                BoundSql boundSql = delegate.getBoundSql();
                Statement statement = CCJSqlParserUtil.parse(boundSql.getSql());
                PlainSelect selectBody = (PlainSelect) ((Select) statement).getSelectBody();
                log.info("原始 sql:{}", boundSql.getSql());
                // 拼接新条件
                buildWhereClause(selectBody, getSql(departAuth));
                ReflectUtil.setFieldValue(boundSql, "sql", statement.toString());
            }
            return invocation.proceed();

        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    /**
     * 添加查询条件
     * @param select
     * @param dataFilter
     * @throws JSQLParserException
     */
    private void buildWhereClause(PlainSelect select, String dataFilter) throws JSQLParserException {
        if(StringUtils.isBlank(dataFilter)) {
            return;
        }
        if (select.getWhere() == null) {
            select.setWhere(CCJSqlParserUtil.parseCondExpression(dataFilter));
        } else {
            AndExpression and = new AndExpression(
                    CCJSqlParserUtil.parseCondExpression(dataFilter), select.getWhere());
            select.setWhere(and);
        }
    }

    private String getSql(DepartAuth departAuth) {
        //结合自己的业务,拼接相对应的sql语句
    }
}

到此这篇关于Mybatis自定义拦截器实现权限功能的文章就介绍到这了,更多相关Mybatis 权限内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文