mybatis拦截器实现数据权限
前端的菜单和按钮权限都可以通过配置来实现,但很多时候,后台查询数据库数据的权限需要通过手动添加SQL来实现。
比如员工打卡记录表,有id,name,dpt_id,company_id等字段,后两个表示部门ID和分公司ID。
查看员工打卡记录SQL为:select id,name,dpt_id,company_id from t_record
当一个总部账号可以查看全部数据此时,sql无需改变。因为他可以看到全部数据。
当一个部门管理员权限员工查看全部数据时,sql需要在末属添加 where dpt_id = #{dpt_id}
如果每个功能模块都需要手动写代码去拿到当前登陆用户的所属部门,然后手动添加where条件,就显得非常的繁琐。
因此,可以通过mybatis的拦截器拿到查询sql语句,再自动改写sql。
mybatis 拦截器
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
分页插件pagehelper就是一个典型的通过拦截器去改写SQL的。

可以看到它通过注解 @Intercepts 和签名 @Signature 来实现,拦截Executor执行器,拦截所有的query查询类方法。
我们可以据此也实现自己的拦截器。
点击查看代码
顺便加了个注解 @SqlLimit,在mapper方法上加了此注解才进行数据权限过滤。
同时注解有两个属性,
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SqlLimit {
/**
* sql表别名
* @return
*/
String alis() default "";
/**
* 通过此列名进行限制
* @return
*/
String columnName() default "";
}
columnName表示通过此列名进行限制,一般来说一个系统,各表当中的此列是统一的,可以忽略。
alis用于标注sql表别名,如 针对sql select * from tablea as a left join tableb as b on a.id = b.id
进行改写,如果不知道表别名,会直接在后面拼接 where dpt_id = #{dptId}
,
那此SQL就会错误的,通过别名 @SqlLimit(alis = "a")
就可以知道需要拼接的是 where a.dpt_id = #{dptId}
执行结果
原SQL:select * from person, 数据权限替换后的SQL:select * from person where dpt_id = 234
原SQL:select * from person where id > 1, 数据权限替换后的SQL:select * from person where dpt_id = 234 and id > 1
但是在使用PageHelper进行分页的时候还是有问题。

可以看到先执行了_COUNT方法也就是PageHelper,再执行了自定义的拦截器。
在我们的业务方法中注入SqlSessionFactory
@Autowired
@Lazy
private List<SqlSessionFactory> sqlSessionFactoryList;

PageInterceptor为1,自定义拦截器为0,跟order相反,PageInterceptor优先级更高,所以越先执行。