本文旨在提供一种实现数据权限控制的思路,整理创建MyBatis拦截器遇到的问题以及解决方式。
1、需求
要求根据用户配置的角色,在指定的查询接口中加入权限控制,比如:
系统管理员可查看所有数据;
部门管理员只能查看本部门的数据;
一般用户只能查看涉及个人的数据;
2、整体设计
2.1、抽象出1个权限类,封装权限等级、查询条件等相关参数
2.2、解析当前登录的账号信息,创建对应的权限对象,放入ThreadLocal中
2.3、创建一个用于标识需要权限控制的注解,打在MyBatis的Mapper接口的方法上
2.4、自定义一个MyBatis拦截器,拦截含有上述注解的query方法,取到ThreadLocal中的权限,在SQL语句中添加额外的where条件
3、问题以及解决方式
3.1、自定义拦截器建好之后,不生效
笔者这里是因为依赖的jar中自定义创建了SqlSessionFactory,创建SqlSessionFactory的代码中没有设置拦截器。
如果项目使用的自定义的SqlSessionFactory,需要创建的时候把拦截器添加进去。
如下代码:
sqlSessionFactoryBean.setPlugins(new Interceptor[]{MyInterceptor});
可参考这篇文章: springboot配置多数据源后mybatis拦截器失效
如果恰好读者的SqlSessionFactory也是在jar中创建的,或者不能直接修改,则需要另一种方式。
新建Configuration配置类,配置类中注入SqlSessionFactory集合,遍历SqlSessionFactory手动添加拦截器。需要注意的是,后添加的拦截器先执行。
@Configuration
public class InterceptorConfiguration {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@PostConstruct
public void addInterceptor() {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
//自己添加自定义的拦截器
configuration.addInterceptor(new MybatisInterceptor());
}
}
}
3.2、自定义拦截器在PageHelper分页之后才执行
由于笔者项目中使用了PageHelper,虽然自定义拦截器生效了,但是是在分页之后拦截的。如何让自定义拦截器在PageHelper之前拦截?
查看PageHelper是如何添加拦截器的,源码如下图:
通过@AutoConfigureAfter注解指定PageHelper配置类在MybatisAutoConfiguration配置类执行之后再执行。
参考PageHelper的代码,修改自定义拦截器的配置类,如下:
@Configuration
@AutoConfigureAfter({PageHelperAutoConfiguration.class})
public class InterceptorAutoConfiguration {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@PostConstruct
public void addInterceptor() {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
configuration.addInterceptor(new MybatisInterceptor());
}
}
}
通过@AutoConfigureAfter({PageHelperAutoConfiguration.class})注解,指定自定义的拦截器在PageHelper配置类执行完后再添加。
在笔者以为大功告成的时候,启动项目,发现还是自定义的配置类先执行。那么问题在哪呢?查阅一番文章后,才知道,@AutoConfigureAfter注解并不是直接加在类上就能用的。
可参考这篇文章具体了解:Spring Boot - 配置排序依赖技巧
也就是说,如果想用@AutoConfigureAfter,自定义的配置类就不能被Spring Boot的启动类扫描到,并且要在spring.factories文件中配置。
其中,有两个注意点:
1、不让配置类被扫描到,可以将配置类放在一个不被扫描的包下,或者将配置类的@Configuration注解去掉。
2、spring.factories文件一定要建在META-INF目录下。
最终配置如下:
@AutoConfigureAfter({PageHelperAutoConfiguration.class})
public class InterceptorAutoConfiguration {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@PostConstruct
public void addInterceptor() {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
configuration.addInterceptor(new MybatisInterceptor());
}
}
}