基于Mybatis 的SAAS应用多租户数据逻辑隔离 package com.opencloud.common.interceptor;import org.apache.commons.lang3.StringUtils;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.plugin.*;import org.apache.ibatis.reflection.DefaultReflectorFactory;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import org.springframework.stereotype.Component;import java.lang.reflect.Field;import java.sql.Connection;import java.util.Properties; /** *多租户数据逻辑隔离 * auth:breka * time:2019-11-01 * 通过sql拦截机制实现多租户之间的数据逻辑隔离,需要先对数据表添加tentant_id(Long)字段以及需要添加逻辑删除的is_delete(tinyint)字段 * 需要在mybatis-config.xml文件里添加此插件,并设置需要隔离的表与需要设置逻辑删除的表 * <plugin interceptor="com.tianque.saas.platform.filter.MybatisSaasInterceptor"> * <!--租户数据隔离表--> * <property name="tentant_filte_tables" value="|uc_sys_organization|uc_sys_position|uc_sys_role|uc_sys_user|"/> * <!--逻辑删除数据表--> * <property name="is_deleted_tables" value="|uc_sys_organization|uc_sys_position|uc_sys_role|uc_sys_user|"/> * </plugin> */@Component@Intercepts({ @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class })})public class MybatisSaasInterceptor implements Interceptor { public static final String SAAS_SQL_SPACE = " "; public static final String SAAS_SQL_SPACE_EQUES_SPACE = " = "; public static final String SAAS_SQL_EQUS = "="; public static final String SAAS_SQL_SPACE_DOT_SPACE = " , "; public static final String SAAS_SQL_DOT = ","; public static final String SAAS_SQL_GOT_N="\n"; public static final String SAAS_SQL_GOT_T="\t"; public static final String SAAS_SQL_COL="|"; public static final String SAAS_SQL_SELECT = "select"; public static final String SAAS_SQL_FROM = "from"; public static final String SAAS_SQL_JOIN = "join"; public static final String SAAS_SQL_WHERE = "where"; public static final String SAAS_SQL_WHERE_SPACE = "where "; public static final String SAAS_SQL_RIGNT_SIGN = ")"; public static final String SAAS_SQL_SPACE_RIGNT_SIGN = " )"; public static final String SAAS_SQL_LEFT_SIGN = "("; public static final String SAAS_SQL_LEFT_SIGN_SPACE = "( "; public static final String SAAS_SQL_INSERT = "insert"; public static final String SAAS_SQL_UPDATE = "update"; public static final String SAAS_SQL_UPDATE_SPACE = "update "; public static final String SAAS_SQL_CREATE_USER = "create_user"; public static final String SAAS_SQL_CREATE_USER_DOT = "create_user,"; public static final String SAAS_SQL_CREATE_DATE = "create_date"; public static final String SAAS_SQL_CREATE_DATE_DOT = "create_date,"; public static final String SAAS_SQL_UPDATE_USER = "update_user"; public static final String SAAS_SQL_UPDATE_DATE = "update_date"; public static final String SAAS_SQL_DELETE = "delete"; public static final String SAAS_SQL_TENTANT_ID = "tentant_id"; public static final String SAAS_SQL_TENTANT_ID_EQUS = "tentant_id="; public static final String SAAS_SQL_SAPCE_TENTANT_ID_EQUS = " tentant_id="; public static final String SAAS_SQL_TENTANT_ID_DOT = "tentant_id,"; public static final String SAAS_SQL_DOT_TENTANT_ID_EQUS = ".tentant_id="; public static final String SAAS_SQL_SPACE_AND_SPACE_TENTANT_ID_EQUS = " and tentant_id="; public static final String SAAS_SQL_DOT_IS_DELETED_EQUS_ZERO_AND_SPACE = ".is_deleted=0 and "; public static final String SAAS_SQL_IS_DELETED_EQUS_ZERO_AND_SPACE = "is_deleted=0 and "; public static final String SAAS_SQL_AS = "as"; public static final String SAAS_SQL_SPACE_SET_SPACE = " set "; public static final String SAAS_SQL_VALUES = "values"; public static final String SAAS_SQL_SPACE_AND_SPANCE = " and "; public static final String SAAS_SQL_UPDATE_USER_EQUS_TAG = "update_user=‘"; public static final String SAAS_SQL_UPDATE_DATE_EQUS_NOW_SIGN_DOT = "update_date=now(),"; public static final String SAAS_SQL_NOW_SIGN_DOT = "now(),"; public static final String SAAS_SQL_SEMEGENT_ONE="‘,update_date=now() where"; public static final String SAAS_SQL_SEMEGENT_TOW=" set is_deleted=1,update_user=‘"; public static final String SAAS_SQL_SQL="sql"; public static final String SAAS_SQL_DELEGATE="delegate.mappedStatement"; private static String tentant_filte_tables=""; //配置租户表 private static String is_deleted_tables=""; //配置逻辑删除表 @Override public Object intercept(Invocation invocation) throws Throwable { String userName="";//当前登入会话用户名 Long tentantId=0l;//当前登入会话租户Id //当前会议用户的租户id附值与用户名附值// if(ThreadVariable.getSession()!=null&&ThreadVariable.getSession().getTentantId()>0)// {// userName=ThreadVariable.getSession().getUserName();// tentantId=ThreadVariable.getSession().getTentantId();// } StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); //先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement MappedStatement mappedStatement = (MappedStatement) metaObject.getValue(SAAS_SQL_DELEGATE); //id为执行的mapper方法的全路径名,如com.uv.dao.UserMapper.insertUser String id = mappedStatement.getId(); //sql语句类型 select、delete、insert、update String sqlCommandType = mappedStatement.getSqlCommandType().toString(); BoundSql boundSql = statementHandler.getBoundSql(); //获取到原始sql语句 String sql = boundSql.getSql(); //得到租户数据隔离后台的Sql String newSql=this.getSaasSql(sql,tentantId,userName); //通过反射修改sql语句 Field field = boundSql.getClass().getDeclaredField(SAAS_SQL_SQL); field.setAccessible(true); field.set(boundSql, newSql); return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { //初始化租户数据隔离表与逻辑删除表 tentant_filte_tables = (String) properties.get("tentant_filte_tables"); is_deleted_tables = (String) properties.get("is_deleted_tables"); System.out.println("tentant_filte_tables:" + tentant_filte_tables + " is_deleted_tables:" + is_deleted_tables); } public int tentant_filte_tables_indexof(String tableName) { return tentant_filte_tables.indexOf(SAAS_SQL_COL+tableName+SAAS_SQL_COL); } private int is_deleted_tables_indexof(String tableName) { return is_deleted_tables.indexOf(SAAS_SQL_COL+tableName+SAAS_SQL_COL); } private String getSaasSql(String sql,Long tentantId,String userName) { sql=sql.toLowerCase().trim(); sql=sql.replace(SAAS_SQL_GOT_N,SAAS_SQL_SPACE) .replace(SAAS_SQL_GOT_T,SAAS_SQL_SPACE) .replace(SAAS_SQL_EQUS,SAAS_SQL_SPACE_EQUES_SPACE) .replace(SAAS_SQL_DOT,SAAS_SQL_SPACE_DOT_SPACE) .replace(SAAS_SQL_LEFT_SIGN_SPACE,SAAS_SQL_LEFT_SIGN) .replace(SAAS_SQL_SPACE_RIGNT_SIGN,SAAS_SQL_RIGNT_SIGN) .replaceAll(" +",SAAS_SQL_SPACE); String newSql = sql; String[] arrSql=sql.split(SAAS_SQL_SPACE); String sqlCommandType =arrSql[0]; if(sqlCommandType.equals(SAAS_SQL_SELECT)) { //region 查询SQL 租户与逻辑删除表替换 for(int i=1;i<arrSql.length;i++) { if(arrSql[i-1].equals(SAAS_SQL_FROM)||arrSql[i-1].equals(SAAS_SQL_JOIN)) { //from where String tableName=arrSql[i]; String smallName=""; if(tableName.indexOf(SAAS_SQL_LEFT_SIGN)==0) continue; if(tentant_filte_tables_indexof(tableName)>-1||is_deleted_tables_indexof(tableName)>-1) { String strWhere=""; //region单表查询 if(arrSql[i+1].equals(SAAS_SQL_WHERE)) { if(tentant_filte_tables.indexOf(tableName)>-1) strWhere+=SAAS_SQL_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE; if(is_deleted_tables.indexOf(tableName)>-1) strWhere+=SAAS_SQL_IS_DELETED_EQUS_ZERO_AND_SPACE; arrSql[i+1]=SAAS_SQL_WHERE_SPACE+strWhere; i++; continue; } if(arrSql[i+2].equals(SAAS_SQL_WHERE)) { smallName=arrSql[i+1]; if(tentant_filte_tables.indexOf(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE; if(is_deleted_tables.indexOf(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_IS_DELETED_EQUS_ZERO_AND_SPACE; arrSql[i+2]=SAAS_SQL_WHERE_SPACE+strWhere; i=i+2; continue; } //endregion //region多表查询 if(arrSql[i+1].equals(SAAS_SQL_AS)) i=i+1; smallName=arrSql[i+1]; if(tentant_filte_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE; if(is_deleted_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_IS_DELETED_EQUS_ZERO_AND_SPACE; if(arrSql[i+2].equals(SAAS_SQL_DOT)) { //region多表查询3表 i=i+3; tableName=arrSql[i]; if(tentant_filte_tables_indexof(tableName)>-1||is_deleted_tables_indexof(tableName)>-1) { if(arrSql[i+1].equals(SAAS_SQL_AS)) i=i+1; smallName=arrSql[i+1]; if(tentant_filte_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE; if(is_deleted_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_IS_DELETED_EQUS_ZERO_AND_SPACE; } if(arrSql[i+2].equals(SAAS_SQL_DOT)) { //多表查询3表 i=i+3; tableName=arrSql[i]; if(tentant_filte_tables_indexof(tableName)>-1||is_deleted_tables_indexof(tableName)>-1) { if(arrSql[i+1].equals(SAAS_SQL_AS)) i=i+1; smallName=arrSql[i+1]; if(tentant_filte_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE; if(is_deleted_tables_indexof(tableName)>-1) strWhere+=smallName+SAAS_SQL_DOT_IS_DELETED_EQUS_ZERO_AND_SPACE; } } //endregion } //endregion for(int j=i;j<arrSql.length;j++) { if(arrSql[j].indexOf(SAAS_SQL_WHERE)==0) { arrSql[j]=SAAS_SQL_WHERE_SPACE+strWhere +SAAS_SQL_SPACE+arrSql[j].replace(SAAS_SQL_WHERE,SAAS_SQL_SPACE); } } } } } newSql= StringUtils.join(arrSql, SAAS_SQL_SPACE); //endregion } else if(sqlCommandType.equals(SAAS_SQL_INSERT)) { //region 新境SQL 租户表与添加人添加时间逻辑, 非查询插入 if(sql.indexOf(SAAS_SQL_SELECT)==-1) { String strTags = ""; String strValues = ""; if (sql.indexOf(SAAS_SQL_CREATE_USER) == -1) { //默认添加创建人 strTags += SAAS_SQL_CREATE_USER_DOT; strValues += "‘" + userName + "‘,"; } if (sql.indexOf(SAAS_SQL_CREATE_DATE) == -1) { //默认添加创建时间 strTags += SAAS_SQL_CREATE_DATE_DOT; strValues += SAAS_SQL_NOW_SIGN_DOT; } String tableName = arrSql[2]; //当前表名 if (tentant_filte_tables_indexof(tableName) > -1) { //是租户过滤表,当前没有添加租房Id插入则进行添加 if (sql.indexOf(SAAS_SQL_TENTANT_ID) == -1) { strTags += SAAS_SQL_TENTANT_ID_DOT; strValues += tentantId + SAAS_SQL_DOT; } } arrSql[3] = SAAS_SQL_LEFT_SIGN + strTags + arrSql[3].replace(SAAS_SQL_LEFT_SIGN, ""); for (int i = 1; i < arrSql.length; i++) { if (arrSql[i].indexOf(SAAS_SQL_LEFT_SIGN) == -1) continue; ; if (arrSql[i - 1].equals(SAAS_SQL_VALUES)) { arrSql[i] = SAAS_SQL_LEFT_SIGN + strValues + arrSql[i].replace(SAAS_SQL_LEFT_SIGN, ""); } //支持批量插入 if (arrSql[i - 1].equals(SAAS_SQL_DOT) && arrSql[i - 2].indexOf(SAAS_SQL_RIGNT_SIGN) > -1) { arrSql[i] = SAAS_SQL_LEFT_SIGN + strValues + arrSql[i].replace(SAAS_SQL_LEFT_SIGN, ""); } } } newSql=StringUtils.join(arrSql, SAAS_SQL_SPACE); //endregion } else if(sqlCommandType.equals(SAAS_SQL_UPDATE)) { //region 更新SQL,租户表与更新人、更新时间替换 String strUpdate=SAAS_SQL_SPACE; if(sql.indexOf(SAAS_SQL_UPDATE_USER)==-1) { if(strUpdate.length()>1) strUpdate+=SAAS_SQL_DOT; strUpdate+=SAAS_SQL_UPDATE_USER_EQUS_TAG+userName+"‘";//默认添加修改人 } if(sql.indexOf(SAAS_SQL_UPDATE_DATE)==-1) { if(strUpdate.length()>1) strUpdate+=SAAS_SQL_DOT; strUpdate+=SAAS_SQL_UPDATE_DATE_EQUS_NOW_SIGN_DOT;//默认添加修改时间 } if(strUpdate.length()>1) { int indexSet=sql.indexOf(SAAS_SQL_SPACE_SET_SPACE); newSql=sql.replace(SAAS_SQL_SPACE_SET_SPACE,SAAS_SQL_SPACE_SET_SPACE+strUpdate); } for(int i=1;i<arrSql.length;i++) { if(arrSql[i-1].equals(SAAS_SQL_UPDATE)) { String tableName=arrSql[i]; //当前表名 if(tentant_filte_tables_indexof(tableName)>-1) { //是租户过滤表,更新条件中没有添加租户ID条件限制,则添加用户ID条件限制 if(sql.indexOf(SAAS_SQL_TENTANT_ID)==-1) { newSql+=SAAS_SQL_SPACE_AND_SPACE_TENTANT_ID_EQUS+tentantId; break; } } } } //endregion } else if(sqlCommandType.equals(SAAS_SQL_DELETE)) { //region 删除SQL 租户表与逻辑删除表逻辑替换; for(int i=1;i<arrSql.length;i++) { if(arrSql[i-1].equals(SAAS_SQL_FROM)) { String tableName=arrSql[i]; //当前表名 if(is_deleted_tables_indexof(tableName)>-1) { //改成逻辑删除 newSql=SAAS_SQL_UPDATE_SPACE+tableName+SAAS_SQL_SEMEGENT_TOW+userName+SAAS_SQL_SEMEGENT_ONE; if(tentant_filte_tables_indexof(tableName)>-1) newSql+=SAAS_SQL_SAPCE_TENTANT_ID_EQUS+tentantId+SAAS_SQL_SPACE_AND_SPANCE;//租户表 int indexWhere=sql.indexOf(SAAS_SQL_WHERE)+5; newSql+=sql.substring(indexWhere,sql.length()); break; } else { //租户物理删除表 if(tentant_filte_tables_indexof(tableName)>-1) { if(sql.indexOf(SAAS_SQL_TENTANT_ID)==-1) { newSql=sql+SAAS_SQL_SPACE_AND_SPACE_TENTANT_ID_EQUS+tentantId;//添加租户ID条件限制 break; } } } } } //endregion } newSql=newSql.replace(SAAS_SQL_SPACE_DOT_SPACE,SAAS_SQL_DOT) .replace(SAAS_SQL_SPACE_EQUES_SPACE,SAAS_SQL_EQUS) .replaceAll(" +",SAAS_SQL_SPACE); System.out.println("---------当前租户:"+ tentantId +"---------"+newSql); return newSql; } }
原文地址:https://www.cnblogs.com/breka/p/11996848.html
时间: 2024-11-19 23:02:38