使用mybatis拦截器处理敏感字段

目录
  • mybatis拦截器处理敏感字段
    • 前言
    • 思路解析
    • 代码
    • 趟过的坑(敲黑板重点)
  • mybatis Excutor 拦截器的使用
    • 这里假设一个场景
    • 实现过程的关键步骤和代码
    • 重点

mybatis拦截器处理敏感字段

前言

由于公司业务要求,需要在不影响已有业务上对 数据库中已有数据的敏感字段加密解密,个人解决方案利用mybatis的拦截器加密解密敏感字段

思路解析

  • 利用注解标明需要加密解密的entity类对象以及其中的数据
  • mybatis拦截Executor.class对象中的query,update方法
  • 在方法执行前对parameter进行加密解密,在拦截器执行后,解密返回的结果

代码

1、配置拦截器(interceptor后为自己拦截器的包路径)

<plugins>
  <plugin interceptor="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">
   <property name="dialectClass" value="com.github.miemiedev.mybatis.paginator.dialect.OracleDialect" />
  </plugin>
  <plugin interceptor="com.XXX.XXXX.service.encryptinfo.DaoInterceptor" />
 </plugins>

2、拦截器的实现

特别注意:因为Dao方法参数有可能单一参数,多参数map形式,以及entity对象参数类型,所以不通类型需有不通的处理方式(本文参数 单一字符串和entity对象,返回的结果集 List<?> 和entity)

后续在拦截器中添加了相应的开关,控制参数是否加密查询,解密已实现兼容

package com.ips.fpms.service.encryptinfo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import com.xxx.xxx.dao.WhiteListDao;
import com.xxx.xxx.entity.db.WhiteListEntity;
import com.xxx.xxx.service.util.SpringBeanUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xxx.xxx.annotation.EncryptField;
import com.xxx.xxx.annotation.EncryptMethod;
import com.xxx.xxx.common.utils.CloneUtil;
import com.xxx.core.psfp.common.support.JsonUtils;
import com.xxx.xxx.service.util.CryptPojoUtils;
@Intercepts({
    @Signature(type=Executor.class,method="update",args={MappedStatement.class,Object.class}),
    @Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
public class EncryptDaoInterceptor implements Interceptor{
	private final Logger logger = LoggerFactory.getLogger(EncryptDaoInterceptor.class);
	private WhiteListDao whiteListDao;
	static int MAPPED_STATEMENT_INDEX = 0;
	static int PARAMETER_INDEX = 1;
	static int ROWBOUNDS_INDEX = 2;
	static int RESULT_HANDLER_INDEX = 3;
	static String ENCRYPTFIELD = "1";
	static String DECRYPTFIELD = "2";
	private static final String ENCRYPT_KEY = "encry146local";
	private static final String ENCRYPT_NUM = "146";
	private static boolean ENCRYPT_SWTICH = true;
	/**
	 * 是否进行加密查询
	 * @return 1 true 代表加密 0 false 不加密
	 */
	private boolean getFuncSwitch(){
		if(whiteListDao == null){
			whiteListDao = SpringBeanUtils.getBean("whiteListDao",WhiteListDao.class);
		}
		try{
			WhiteListEntity entity = whiteListDao.selectOne(ENCRYPT_KEY,ENCRYPT_NUM);
			if(entity!=null && "1".equals(entity.getFlag())){
				ENCRYPT_SWTICH = true;
			}else{
				ENCRYPT_SWTICH = false;
			}
		}catch (Exception e){
			logger.error(this.getClass().getName()+".getFuncSwitch 白名单查询异常,默认本地数据加密关闭[]:",e.getStackTrace());
			return false;
		}
		return ENCRYPT_SWTICH;
	}
	/**
	 * 校验执行器方法 是否在白名单中
	 * @param statementid
	 * @return true 包含 false 不包含
	 */
	private boolean isWhiteList(String statementid){
		boolean result = false;
		String whiteStatementid = "com.ips.fpms.dao.WhiteListDao.selectOne";
		if(whiteStatementid.indexOf(statementid)!=-1){
			result = true;
		}
		return result;
	}
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		logger.info("EncryptDaoInterceptor.intercept开始执行==> ");
		MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];
		Object parameter = invocation.getArgs()[PARAMETER_INDEX];
		logger.info(statement.getId()+"未加密参数串:"+JsonUtils.object2jsonString(CloneUtil.deepClone(parameter)));
		/*
		 *
		 * 判断是否拦截白名单 或 加密开关是否配置,
		 * 如果不在白名单中,并且本地加密开关 已打开 执行参数加密
		 *
		 *  */
		if(!isWhiteList(statement.getId()) && getFuncSwitch()){
			parameter = encryptParam(parameter, invocation);
			logger.info(statement.getId()+"加密后参数:"+JsonUtils.object2jsonString(CloneUtil.deepClone(parameter)));
		}
		invocation.getArgs()[PARAMETER_INDEX] = parameter;
		Object returnValue = invocation.proceed();
		logger.info(statement.getId()+"未解密结果集:"+JsonUtils.object2jsonString(CloneUtil.deepClone(returnValue)));
		returnValue = decryptReslut(returnValue, invocation);
		logger.info(statement.getId()+"解密后结果集:"+JsonUtils.object2jsonString(CloneUtil.deepClone(returnValue)));
		logger.info("EncryptDaoInterceptor.intercept执行结束==> ");
		return returnValue;
	}
	/**
	 * 解密结果集
	 * @param @param returnValue
	 * @param @param invocation
	 * @param @return
	 * @return Object
	 * @throws
	 *
	 */
	public Object decryptReslut(Object returnValue,Invocation invocation){
		MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];
		if(returnValue!=null){
	       	 if(returnValue instanceof ArrayList<?>){
	       		 List<?> list = (ArrayList<?>) returnValue;
	       		 List<Object> newList  = new ArrayList<Object>();
	       		 if (1 <= list.size()){
	       			 for(Object object:list){
	       				 Object obj = CryptPojoUtils.decrypt(object);
	       				 newList.add(obj);
	       			 }
	       			 returnValue = newList;
	       		 }
	       	 }else if(returnValue instanceof Map){
	       		String[] fields = getEncryFieldList(statement,DECRYPTFIELD);
	       		if(fields!=null){
	       			returnValue = CryptPojoUtils.getDecryptMapValue(returnValue,fields);
	       		}
	       	 }else{
	       		 returnValue = CryptPojoUtils.decrypt(returnValue);
	       	 }
        }
		return returnValue;
	}
	/***
	 * 针对不同的参数类型进行加密
	 * @param @param parameter
	 * @param @param invocation
	 * @param @return
	 * @return Object
	 * @throws
	 *
	 */
	public Object encryptParam(Object parameter,Invocation invocation){
		MappedStatement statement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];
		try {
			if(parameter instanceof String){
				if(isEncryptStr(statement)){
					parameter = CryptPojoUtils.encryptStr(parameter);
				}
			}else if(parameter instanceof Map){
				String[] fields = getEncryFieldList(statement,ENCRYPTFIELD);
				if(fields!=null){
					parameter = CryptPojoUtils.getEncryptMapValue(parameter,fields);
				}
			}else{
				parameter = CryptPojoUtils.encrypt(parameter);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			logger.info("EncryptDaoInterceptor.encryptParam方法异常==> " + e.getMessage());
		}
		return parameter;
	}
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	@Override
	public void setProperties(Properties properties) {
	}
	/**
	 * 获取参数map中需要加密字段
	 * @param statement
	 * @param type
	 * @return List<String>
	 * @throws
	 *
	 */
	private String[] getEncryFieldList(MappedStatement statement,String type){
		String[] strArry = null;
		Method method = getDaoTargetMethod(statement);
		Annotation annotation =method.getAnnotation(EncryptMethod.class);
		if(annotation!=null){
			if(type.equals(ENCRYPTFIELD)){
				String encryString = ((EncryptMethod) annotation).encrypt();
				if(!"".equals(encryString)){
					strArry =encryString.split(",");
				}
			}else if(type.equals(DECRYPTFIELD)){
				String encryString = ((EncryptMethod) annotation).decrypt();
				if(!"".equals(encryString)){
					strArry =encryString.split(",");
				}
			}else{
				strArry = null;
			}
		}
		return strArry;
	}
	/**
	 * 获取Dao层接口方法
	 * @param @return
	 * @return Method
	 * @throws
	 *
	 */
	private Method getDaoTargetMethod(MappedStatement mappedStatement){
		Method method = null;
		try {
			String namespace = mappedStatement.getId();
		    String className = namespace.substring(0,namespace.lastIndexOf("."));
		    String methedName= namespace.substring(namespace.lastIndexOf(".") + 1,namespace.length());
			Method[] ms = Class.forName(className).getMethods();
			for(Method m : ms){
		        if(m.getName().equals(methedName)){
		        	method = m;
		        	break;
		        }
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			logger.info("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " + e.getMessage());
			return method;

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			logger.info("EncryptDaoInterceptor.getDaoTargetMethod方法异常==> " + e.getMessage());
			return method;
		}
		return method;
	}
	/**
	 * 判断字符串是否需要加密
	 * @param @param mappedStatement
	 * @param @return
	 * @return boolean
	 * @throws
	 *
	 */
	private boolean isEncryptStr(MappedStatement mappedStatement) throws ClassNotFoundException{
		boolean reslut = false;
		try {
				Method m = getDaoTargetMethod(mappedStatement);
	        	m.setAccessible(true);
	        	Annotation[][] parameterAnnotations = m.getParameterAnnotations();
		        	 if (parameterAnnotations != null && parameterAnnotations.length > 0) {
		        		 for (Annotation[] parameterAnnotation : parameterAnnotations) {
		        	         for (Annotation annotation : parameterAnnotation) {
		        	          if (annotation instanceof EncryptField) {
		        	        	  reslut = true;
		        	          }
		        	      }
		        	 }
	        	 }
		} catch (SecurityException e) {
			e.printStackTrace();
			logger.info("EncryptDaoInterceptor.isEncryptStr异常:==> " + e.getMessage());
			reslut = false;
		}
		return reslut;
	}
}

2、注解的entity对象

//是否需要加密解密对象
@EncryptDecryptClass
public class MerDealInfoRequest extends PagingReqMsg {
//属性定义
 @EncryptField
    @DecryptField
    private String cardNo;
}

3、dao方法中的单一参数

List<Dealer> selectDealerAndMercode(@EncryptField String idcardno);

4、封装的工具类(EncryptDecryptUtil.decryptStrValue 解密方法 EncryptDecryptUtil.decryptStrValue 加密方法)

package com.xxx.xxx.service.util;
import java.lang.reflect.Field;
import java.util.ArrayList;
import org.apache.commons.lang.StringUtils;
import org.apache.pdfbox.Encrypt;
import org.apache.poi.ss.formula.functions.T;
import com.xxx.xxx.annotation.DecryptField;
import com.xxx.xxx.annotation.EncryptDecryptClass;
import com.xxx.xxx.annotation.EncryptField;
import com.xxx.xxx.common.utils.EncryptDecryptUtil;
public class CryptPojoUtils {
	 /**
     * 对象t注解字段加密
     * @param t
     * @param <T>
     * @return
     */
    public static <T> T encrypt(T t) {
    	if(isEncryptAndDecrypt(t)){
	        Field[] declaredFields = t.getClass().getDeclaredFields();
	        try {
	            if (declaredFields != null && declaredFields.length > 0) {
	                for (Field field : declaredFields) {
	                    if (field.isAnnotationPresent(EncryptField.class) && field.getType().toString().endsWith("String")) {
	                        field.setAccessible(true);
	                        String fieldValue = (String) field.get(t);
	                        if (StringUtils.isNotEmpty(fieldValue)) {
	    	                    field.set(t, EncryptDecryptUtil.encryStrValue(fieldValue) );
	                        }
	                        field.setAccessible(false);
	                    }
	                }
	            }
	        } catch (IllegalAccessException e) {
	            throw new RuntimeException(e);
	        }
    	}
        return t;
    }
    /**
     * 加密单独的字符串
     *
     * @param @param t
     * @param @return
     * @return T
     * @throws
     *
     */
    public static <T> T EncryptStr(T t){
    	if(t instanceof String){
    		t = (T) EncryptDecryptUtil.encryStrValue((String) t);
    	}
    	return t;
    }
    /**
     * 对含注解字段解密
     * @param t
     * @param <T>
     */
    public static <T> T decrypt(T t) {
    	if(isEncryptAndDecrypt(t)){
	        Field[] declaredFields = t.getClass().getDeclaredFields();
	        try {
	            if (declaredFields != null && declaredFields.length > 0) {
	                for (Field field : declaredFields) {
	                    if (field.isAnnotationPresent(DecryptField.class) && field.getType().toString().endsWith("String")) {
	                        field.setAccessible(true);
	                        String fieldValue = (String)field.get(t);
	                        if(StringUtils.isNotEmpty(fieldValue)) {
	                            field.set(t, EncryptDecryptUtil.decryptStrValue(fieldValue));
	                        }
	                    }
	                }
	            }
	        } catch (IllegalAccessException e) {
	            throw new RuntimeException(e);
	        }
        }
         return t;
    }
    /**
     * 判断是否需要加密解密的类
     * @param @param t
     * @param @return
     * @return Boolean
     * @throws
     *
     */
    public static <T> Boolean isEncryptAndDecrypt(T t){
    	Boolean reslut = false;
    	if(t!=null){
    		Object object = t.getClass().getAnnotation(EncryptDecryptClass.class);
        	if(object != null){
        		reslut = true;
        	}
    	}
		return reslut;
    }
}

趟过的坑(敲黑板重点)

1、在实现上述功能后的测试中,其中select查询方法的参数在加密成功后,但是Executor执行器执行方法参数依旧为未加密的参数,找各路大神都没有解决的思路,最后发现项目中引用了开源的分页插件, OffsetLimitInterceptor拦截器把参数设置成为final的,所以自定义拦截器没有修改成功这个sql参数;

解决办法:自定义拦截器放到这个拦截器后,自定义拦截器先执行就可以了

<plugins>
     //就是这个拦截器
  <plugin interceptor="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">
   <property name="dialectClass" value="com.github.miemiedev.mybatis.paginator.dialect.OracleDialect" />
  </plugin>
  <plugin interceptor="com.ips.fpms.service.encryptinfo.DaoInterceptor" />
 </plugins>
public Object intercept(final Invocation invocation) throws Throwable {
        final Executor executor = (Executor) invocation.getTarget();
        final Object[] queryArgs = invocation.getArgs();
        final MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
        //拦截器把参数设置成为final的,所以自定义拦截器没有修改到这个参数
        final Object parameter = queryArgs[PARAMETER_INDEX];
        final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];
        final PageBounds pageBounds = new PageBounds(rowBounds);
        final int offset = pageBounds.getOffset();
        final int limit = pageBounds.getLimit();
        final int page = pageBounds.getPage();
        .....省略代码....
        }

2、数据库存量数据处理

在添加拦截器后,必须对数据库的存量数据进行处理,如果不进行处理,查询参数已经加密,但是数据依旧是明文,会导致查询条件不匹配

mybatis Excutor 拦截器的使用

这里要讲的巧妙用法是用来实现在拦截器中执行额外 MyBatis 现有方法的用法。

并且会提供一个解决拦截Executor时想要修改MappedStatement时解决并发的问题。

这里假设一个场景

实现一个拦截器,记录 MyBatis 所有的 insert,update,delete 操作,将记录的信息存入数据库。

这个用法在这里就是将记录的信息存入数据库。

实现过程的关键步骤和代码

1.首先在某个 Mapper.xml 中定义好了一个往日志表中插入记录的方法,假设方法为id="insertSqlLog"。

2.日志表相关的实体类为SqlLog.

3.拦截器签名:

@Intercepts({@org.apache.ibatis.plugin.Signature(
    type=Executor.class,
    method="update",
    args={MappedStatement.class, Object.class})})
public class SqlInterceptor implements Interceptor

4.接口方法简单实现:

public Object intercept(Invocation invocation) throws Throwable {
    Object[] args = invocation.getArgs();
    MappedStatement ms = (MappedStatement) args[0];
    Object parameter = args[1];
    SqlLog log = new SqlLog();
    Configuration configuration = ms.getConfiguration();
    Object target = invocation.getTarget();
    StatementHandler handler = configuration.newStatementHandler((Executor) target, ms,
              parameter, RowBounds.DEFAULT, null, null);
    BoundSql boundSql = handler.getBoundSql();
    //记录SQL
    log.setSqlclause(boundSql.getSql());
    //执行真正的方法
    Object result = invocation.proceed();
    //记录影响行数
    log.setResult(Integer.valueOf(Integer.parseInt(result.toString())));
    //记录时间
    log.setWhencreated(new Date());
    //TODO 还可以记录参数,或者单表id操作时,记录数据操作前的状态
    //获取insertSqlLog方法
    ms = ms.getConfiguration().getMappedStatement("insertSqlLog");
    //替换当前的参数为新的ms
    args[0] = ms;
    //insertSqlLog 方法的参数为 log
    args[1] = log;
    //执行insertSqlLog方法
    invocation.proceed();
    //返回真正方法执行的结果
    return result;
}

重点

MappedStatement是一个共享的缓存对象,这个对象是存在并发问题的,所以几乎任何情况下都不能去修改这个对象(通用Mapper除外),想要对MappedStatement做修改该怎么办呢?

并不难,Executor中的拦截器方法参数中都有MappedStatement ms,这个ms就是后续方法执行要真正用到的MappedStatement,这样一来,问题就容易解决了,根据自己的需要,深层复制MappedStatement对象中自己需要修改的属性,然后修改这部分属性,之后将修改后的ms通过上面代码中args[0]=ms这种方式替换原有的参数,这样就能实现对ms的修改而且不会有并发问题了。

这里日志的例子就是一个更简单的应用,并没有创建ms,只是获取了一个新的ms替换现有的ms,然后去执行。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Mybatis Interceptor 拦截器的实现

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件. 拦截器(Interceptor)在 Mybatis 中被当做插件(plugin)对待,官方文档提供了 Executor,ParameterHandler,ResultSetHandler,StatementHandler 共4种,并且提示"这些类中方法的细

  • mybatis拦截器实现通用权限字段添加的方法

    实现效果 日常sql中直接使用权限字段实现权限内数据筛选,无需入参,直接使用,使用形式为: select * from crh_snp.channelinfo where short_code in (${commonEnBranchNo}) 注意事项说明 1.添加插件若使用xml形式mybatis可在配置文件中plugins标签中添加,本项目实际使用的为注解形式mybatis,需要通过SqlSessionFactoryBean代码方式添加或者SqlSessionFactoryBean的xml配

  • MyBatis拦截器:给参数对象属性赋值的实例

    该拦截器的作用:在进行增加.修改等操作时,给数据模型的一些通用操作属性(如:创建人.创建时间.修改人.修改时间等)自动赋值. 该实现是在DAO层拦截,即存入DB前最后一层.后经分析,不是很合理,改为在service层拦截,用spring AOP来实现了,该代码遂弃用.不过已经测试可用,记录备忘. package com.development; import java.lang.reflect.InvocationTargetException; import java.util.Date; i

  • MyBatis Excutor 拦截器的巧妙用法

    这里要讲的巧妙用法是用来实现在拦截器中执行额外 MyBatis 现有方法的用法. 并且会提供一个解决拦截Executor时想要修改MappedStatement时解决并发的问题. 这里假设一个场景: 实现一个拦截器,记录 MyBatis 所有的 insert,update,delete 操作,将记录的信息存入数据库. 这个用法在这里就是将记录的信息存入数据库. 实现过程的关键步骤和代码: 1.首先在某个 Mapper.xml 中定义好了一个往日志表中插入记录的方法,假设方法为id="insert

  • 使用mybatis拦截器处理敏感字段

    目录 mybatis拦截器处理敏感字段 前言 思路解析 代码 趟过的坑(敲黑板重点) mybatis Excutor 拦截器的使用 这里假设一个场景 实现过程的关键步骤和代码 重点 mybatis拦截器处理敏感字段 前言 由于公司业务要求,需要在不影响已有业务上对 数据库中已有数据的敏感字段加密解密,个人解决方案利用mybatis的拦截器加密解密敏感字段 思路解析 利用注解标明需要加密解密的entity类对象以及其中的数据 mybatis拦截Executor.class对象中的query,upd

  • 详解Mybatis拦截器安全加解密MySQL数据实战

    需求背景 公司为了通过一些金融安全指标(政策问题)和防止数据泄漏,需要对用户敏感数据进行加密,所以在公司项目中所有存储了用户信息的数据库都需要进行数据加密改造.包括Mysql.redis.mongodb.es.HBase等. 因为在项目中是使用springboot+mybatis方式连接数据库进行增删改查,并且项目是中途改造数据.所以为了不影响正常业务,打算这次改动尽量不侵入到业务代码,加上mybatis开放的各种拦截器接口,所以就以此进行改造数据. 本篇文章讲述如何在现有项目中尽量不侵入业务方

  • Mybatis拦截器实现分页

    最终dao层结果: public interface ModelMapper { Page<Model> pageByConditions(RowBounds rowBounds, Model record); } 接下来一步一步来实现分页. 一.创建Page对象: public class Page<T> extends PageList<T> { private int pageNo = 1;// 页码,默认是第一页 private int pageSize = 1

  • MyBatis拦截器的原理与使用

    目录 二.拦截器注册的三种方式 1.XML注册 2.配置类注册 3.注解方式 三.ParameterHandler参数改写-修改时间和修改人统一插入 四.通过StatementHandler改写SQL 一.拦截对象和接口实现示例 MyBatis拦截器的作用是在于Dao到DB中间进行额外的处理.大部分情况下通过mybatis的xml配置sql都可以达到想要的DB操作效果,然而存在一些类似或者相同的查询条件或者查询要求,这些可以通过拦截器的实现可以提升开发效率,比如:分页.插入和更新时间/人.数据权

  • 学好Java MyBatis拦截器,提高工作效率

    目录 场景: 1.麻瓜做法 2.优雅做法 3.什么是拦截器? 4.使用拦截器更新审计字段 5.自定义拦截器 6.配置拦截器插件 场景: 在后端服务开发时,现在很流行的框架组合就是SSM(SpringBoot + Spring + MyBatis),在我们进行一些业务系统开发时,会有很多的业务数据表,而表中的信息从新插入开始,整个生命周期过程中可能会进行很多次的操作. 比如:我们在某网站购买一件商品,会生成一条订单记录,在支付完金额后订单状态会变为已支付,等最后我们收到订单商品,这个订单状态会变成

  • Mybatis拦截器实现数据权限的示例代码

    在我们日常开发过程中,通常会涉及到数据权限问题,下面以我们常见的一种场景举例: 一个公司有很多部门,每个人所处的部门和角色也不同,所以数据权限也可能不同,比如超级管理员可以查看某张 表的素有信息,部门领导可以查看该部门下的相关信息,部门普通人员只可以查看个人相关信息,而且由于角色的 不同,各个角色所能查看到的数据库字段也可能不相同,那么此处就涉及到了数据权限相关的问题.那么我们该如 何处理数据权限相关的问题呢?我们提供一种通过Mybatis拦截器实现的方式,下面我们来具体实现一下 pom.xml

  • springboot+mybatis拦截器方法实现水平分表操作

    目录 1.前言 2.MyBatis 允许使用插件来拦截的方法 3.Interceptor接口 4分表实现 4.1.大体思路 4.2.1 Mybatis如何找到我们新增的拦截服务 4.2.2 应该拦截什么样的对象 4.2.3 实现自定义拦截器 4.2.逐步实现 1.前言 业务飞速发展导致了数据规模的急速膨胀,单机数据库已经无法适应互联网业务的发展.由于MySQL采用 B+树索引,数据量超过阈值时,索引深度的增加也将使得磁盘访问的 IO 次数增加,进而导致查询性能的下降:高并发访问请求也使得集中式数

  • mybatis拦截器及不生效的解决方法

    目录 背景: mybatis拦截器怎样做 定义一个拦截器 定义一个 MybatisInterceptorAutoConfiguration 背景: 在一些需求下,使用拦截器会大大简化工作量也更加灵活: 在项目中,要更新数据表的审计字段,比如 create_time, creator, update_time, updator, 这些字段,如果每一个表对应的mapper 都去写一次,或每一个方法都去更新一下,这个工作量非常大并且不太友好,并且不够优雅. 记录一些日志,比如执行sql时侯,要打印每一

  • java MyBatis拦截器Inteceptor详细介绍

    有许多java初学者对于MyBatis拦截器Inteceptor不是很了解,在这里我来为各位整理下篇关于java中MyBatis拦截器Inteceptor详解, 本文主要分析MyBatis的插件机制,实际就是Java动态代理实现的责任链模式实现. 根据官方文档.Mybatis只允许拦截以下方法,这个决定写拦截器注解签名参数. 代码如下 Executor (update, query, flushStatements, commit, rollback, getTransaction, close

随机推荐