Mybatis⾃定义拦截实现注解⽅式数据权限拦截
问题⼀:和分页的冲突,造成执⾏不了的问题
刚开始解决⽅法是
aboatedu.interceptor.ExecutorInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import t.ApplicationListener;
import t.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class StartSysListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger log = Logger(StartSysListener.class);
@Autowired
private ExecutorInterceptor executorInterceptor;
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@Overridelenovo首页
public void onApplicationEvent(ContextRefreshedEvent event) {
this.addMyInterceptor();
}
private void addMyInterceptor() {
log.debug("添加⾃定义Mybatis SQL.");
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
}
}
}
后⾯发现要么SQL就拼接不上要么,要么SQL就重复拼接。后来查看了PageHelper的官⽅对的说明:
把的定义按他的规范来使⽤,然⽽能执⾏,也不需要上⾯的starterListner的监听。但是问题还是⼀样,拼接SQL会重复。后来查了些资料,得到如下解决⽅案:
@Configuration
@AutoConfigureAfter(PageHelperAutoConfiguration.class)
public class TestLogAutoConfiguration {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@PostConstruct
public void addMyInterceptor() {
DataPermissionInterceptor e = new DataPermissionInterceptor();
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
}
}
}
原则就是在加载PageHelper的配置之后加载该配置,添加,保证执⾏顺序。问题⼆:如何拦截SQL,实现权限SQL语句拼接
⾸先肯定需要进⾏SQL拦截,必然的不能少,所以第⼀步,⾃定义
源码下载网站模板/**
* 封装基础,针对不同的操作进⾏不同的处理
*/
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, Bo }
)
@Slf4j
public class DataPermissionInterceptor implements Interceptor {
private static final Integer MAPPED_STATEMENT_INDEX = 0;
private static final Integer PARAM_OBJ_INDEX = 1;
private static final Integer ROW_BOUNDS_INDEX = 2;
private static final Integer RESULT_HANDLER_INDEX = 3;
private static final Integer CACHE_KEY_INDEX = 4;
private static final Integer BOUND_SQL_INDEX = 5;
private static final String COUNT_PRE = "_COUNT";
@Autowired
private SpringUtil springUtil;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = Args();
MappedStatement ms = (MappedStatement) args[MAPPED_STATEMENT_INDEX];
Object parameter = args[PARAM_OBJ_INDEX];
RowBounds rowBounds = (RowBounds) args[ROW_BOUNDS_INDEX];
mysql语句的执行顺序ResultHandler resultHandler = (ResultHandler) args[RESULT_HANDLER_INDEX];
Executor executor = (Executor) Target();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进⼊⼀次
if(args.length == 4){
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = ateCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[CACHE_KEY_INDEX];
boundSql = (BoundSql) args[BOUND_SQL_INDEX];
}
return executor.query(newMappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
然后考虑第⼀个问题,不是所有的查询都要拦截,那么就必然是标注了的才需要去拦截。
⽬标明确,解决⽅法也就很明确了:注解
⾃定义注解如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataAuth {
String empId();
String project();
String org() default "";
String orderBy();
}
这个注解很好理解,作⽤域是⽅法级别,就是要查那个员⼯的,那个部门的,那个项⽬的,最后再排序下。java数据接口怎么写
OK有了注解,就需要在⾥⾯进⾏捕获该注解:
3组随机数字表/**
* 获取数据权限注解信息
*
* @param mappedStatement
* @return
*/
private DataAuth getPermissionByDelegate(MappedStatement mappedStatement) {
DataAuth dataAuth = null;
try {
String id = Id();
//统计SQL取得注解也是实际查询id上得注解,所以需要去掉_COUNT
ains(COUNT_PRE)){
id = id.replace(COUNT_PRE,"");
}
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1, id.length());
final Class<?> cls = Class.forName(className);
final Method[] method = Methods();
for (Method me : method) {
if (me.getName().equals(methodName) && me.isAnnotationPresent(DataAuth.class)) {
dataAuth = me.getAnnotation(DataAuth.class);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return dataAuth;
}
这个就是反射了,没什么很难理解的,唯⼀需要注意的细节就是,如果是使⽤PageHelper的话,会默认先⽣存⼀个查询总数的SQL。这⾥需要共⽤改注解,⽽PageHelper的⽣成查询总量的⽅法其实就在该查询ID的后⾯追加_count
private static final String COUNT_PRE = "_COUNT";
所以这⾥捕获注解的时候需要去原查询的Id上去捕获,保证统计⽅法也能拼接上权限SQL。
考虑第⼆个问题:如何拼接SQL,也就是捕获原SQL⽣成新SQL
获取原SQL,这个⽐较简单
//原sql
String sql = Sql();
拼接SQL得到新SQL
支持小数点的进制转换器String newSql = permissionSql(sql,dataAuth,boundSql);
/**
* 权限关联
* @param sql
* @param dataAuth
* @param boundSql
*/
private String permissionSql(String sql,DataAuth dataAuth, BoundSql boundSql) {
String oauthSql = pId(),dataAuth.project(),()); log.info("权限SQL:{}"+oauthSql);
String newSql = sql +oauthSql + derBy();
return newSql;
}
/**
* 获取数据权限
* @param ownerField
* @param projectFiled
* @return
*/
private String getDataPermission(String ownerField,String projectFiled,String orgIdField){ BaseEmployee employee = BaseEmployee();
String dataPermission = CurrentMenuCodeDataPermission();
String sql = "";
if(Code().equals(dataPermission)){
if(StringUtils.isNotBlank(ownerField)) {
sql += " and " + ownerField + " = '" + Id() + "' ";
}
if(StringUtils.isNotBlank(orgIdField)){
sql += " and " + orgIdField + " = '" + OrgId() + "' ";
}
log.info("当前个⼈权限");
}
//本部及以下
if(Code().equals(dataPermission)) {
//当前登录⼈所在组织和所有下级组织
String suborg = SubOrg();
if(StringUtils.isNotBlank(orgIdField)){
sql += " and find_in_set("+orgIdField+",'"+suborg+"')>0";
}
String employeeMyOrgAndSubordinate = SubOrgEmp();
if(StringUtils.isNotBlank(ownerField)){
sql += " and find_in_set("+ownerField+",'"+employeeMyOrgAndSubordinate+"')>0";
}
}
//全部
if(Code().equals(dataPermission)){
//当前登录⼈所在的项⽬(任意上级有项⽬属性都可以,每个⼈只能看⾃⼰所在项⽬的数据)
String project = Project();
if(project!=null) {
}
}
//本军团
if(Code().equals(dataPermission)){
String groupOrgs = GroupOrg();
if(StringUtils.isNotBlank(orgIdField)){
sql += " and find_in_set("+orgIdField+",'"+groupOrgs+"')>0";
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论