├── README.md ├── build.gradle ├── settings.gradle └── src └── main └── java └── com.chj.common ├── Interface └── DataFilterInterface.java ├── mybatis ├── DPHelper.java ├── DataPermission.java ├── PermissionHandler.java ├── annotation │ ├── DataPermissionAop.java │ ├── DataPermissionTable.java │ └── UnDataPermissionTable.java ├── aspect │ ├── DataPermissionAspect.java │ ├── DataPermissionFilterAspect.java │ └── DataUnPermissionFilterAspect.java ├── interceptor │ ├── DataPermissionInterceptor.java │ ├── ParamInterceptor.java │ └── ResultInterceptor.java └── visitor │ ├── ExpressionVisitorImpl.java │ ├── FromItemVisitorImpl.java │ ├── ItemsListVisitorImpl.java │ ├── SelectItemVisitorImpl.java │ └── SelectVisitorImpl.java ├── registrar └── DataPermissionRegistrar.java ├── sys ├── ClientInfo.java ├── ColumnInfo.java ├── SessionUser.java └── TableInfo.java └── utils ├── DataTypeUtils.java ├── ReflectUtil.java ├── SqlSpliceUtils.java └── StringUtils.java /README.md: -------------------------------------------------------------------------------- 1 | # DataPermissionUtils 2 | 使用 mybatis Interceptor 的一个数据权限工具包 3 | 4 | 5 | 目前实现: 6 | 1.参数拦截 7 | 2.sql拦截 8 | 3.查询结果拦截 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | version '1.0-SNAPSHOT' 6 | 7 | sourceCompatibility = 1.8 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | //指定编译的编码 13 | tasks.withType(JavaCompile) { 14 | options.encoding = "UTF-8" 15 | } 16 | group 'DataPermissionUtils' 17 | version '1.0-SNAPSHOT' 18 | 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | dependencies { 27 | compile('org.projectlombok:lombok:1.16.20') 28 | compile('net.sf.json-lib:json-lib:2.4:jdk15') 29 | compile('com.alibaba:fastjson:1.2.44') 30 | compile('org.apache.commons:commons-lang3:3.6') 31 | compile('commons-httpclient:commons-httpclient:3.0') 32 | compile('com.netflix.feign:feign-httpclient:8.3.0') 33 | compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.10') 34 | compile('com.fasterxml.jackson.core:jackson-annotations:2.8.10') 35 | compile('org.springframework:spring-context:4.3.17.RELEASE') 36 | compile('io.springfox:springfox-swagger2:2.7.0') 37 | compile('io.springfox:springfox-swagger-ui:2.7.0') 38 | compile('javax.servlet:javax.servlet-api:3.0.1') 39 | compile('cn.afterturn:easypoi-web:3.2.0') 40 | compile('net.sf.dozer:dozer:5.5.1') 41 | compile('org.springframework:spring-web:4.3.13.RELEASE') 42 | compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1' 43 | compile('com.github.jsqlparser:jsqlparser:1.1') 44 | compile('org.aspectj:aspectjweaver:1.8.10') 45 | compile('ch.qos.logback:logback-classic:1.2.3') 46 | testCompile group: 'junit', name: 'junit', version: '4.12' 47 | } 48 | 49 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'DataPermissionUtils' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/Interface/DataFilterInterface.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.Interface; 2 | 3 | import com.chj.common.mybatis.DataPermission; 4 | 5 | /** 6 | * @author chenhj 7 | * 数据权限业务系统实现接口 8 | **/ 9 | public interface DataFilterInterface { 10 | 11 | /** 12 | * 13 | * @return 返回DataPermission 如果没有数据权限过滤要求,返回null 14 | * 15 | */ 16 | DataPermission queryDataPermission(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/DPHelper.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author chenhj 7 | * @description 数据权限开启 8 | * @create 2018-05-02 下午7:13 9 | */ 10 | public class DPHelper { 11 | protected static final ThreadLocal DATA_PERMISSION = new ThreadLocal<>(); 12 | 13 | protected static final ThreadLocal CHANGE_TABLE = new ThreadLocal<>(); 14 | 15 | /** 给当前线程设置ThreadLocal变量,设置权限 16 | * @param permissionHandler 提供需要进行数据控制的表及对应有权限的id和字段 17 | * @param tables 需要进行数据控制的表 18 | */ 19 | public static void start(PermissionHandler permissionHandler, String... tables) { 20 | //获取权限数据 21 | setLocalDataPermissions(permissionHandler.getPermission(tables)); 22 | } 23 | 24 | public static DataPermission getLocalDataPermissions() { 25 | return DATA_PERMISSION.get(); 26 | } 27 | 28 | public static void setLocalDataPermissions(DataPermission dataPermissions) { 29 | DATA_PERMISSION.set(dataPermissions); 30 | } 31 | 32 | /** 33 | * 需要手动清除当前线程的权限信息 34 | */ 35 | public static void clearDataPermissions() { 36 | DATA_PERMISSION.remove(); 37 | } 38 | 39 | /** 40 | * 判断是否修改表结构 41 | * 42 | * @return 43 | */ 44 | public static Boolean getChangeTable() { 45 | Boolean result = CHANGE_TABLE.get(); 46 | if (Objects.isNull(result)) { 47 | result = false; 48 | setChangeTable(result); 49 | } 50 | return result; 51 | } 52 | 53 | /**是否修改表结构 54 | * @param isChange 55 | */ 56 | public static void setChangeTable(Boolean isChange) { 57 | CHANGE_TABLE.set(isChange); 58 | } 59 | 60 | /** 61 | * 清除当前线程是否修改表结构信息 62 | */ 63 | public static void clearChangeTable() { 64 | CHANGE_TABLE.remove(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/DataPermission.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis; 2 | 3 | import com.chj.common.sys.TableInfo; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * @author chenhj 11 | * @description 数据权限bean 12 | * @create 2018-05-02 下午7:18 13 | */ 14 | @Data 15 | public class DataPermission implements Serializable { 16 | private static final long serialVersionUID = 1L; 17 | /** 18 | * key为需要进行权限控制的表名,value表示用户可以查询的记录和对应的字段 19 | */ 20 | private List tables; 21 | /** 22 | * 是否是管理员,如果是管理员,不进行数据过滤(数据权限的白名单用户) 23 | */ 24 | private Boolean admin = false; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/PermissionHandler.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis; 2 | 3 | /** 4 | * @author: chenhj 5 | * @date: 18-5-7 下午4:16 6 | * @desciptions: 获取数据权限的接口 7 | */ 8 | public interface PermissionHandler { 9 | /**获得权限 10 | * @param tables 表 11 | * @return 12 | */ 13 | DataPermission getPermission(String... tables); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/annotation/DataPermissionAop.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.annotation; 2 | 3 | 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 开启数据权限 11 | */ 12 | @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) 13 | @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE}) 14 | @Documented 15 | public @interface DataPermissionAop { 16 | boolean enabled() default true; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/annotation/DataPermissionTable.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | /** 8 | * 加在其他方法上可以使用AOP做切面处理 9 | * 只针对某些表进行数据权限处理 10 | */ 11 | @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) 12 | @Target({ElementType.METHOD, ElementType.TYPE}) 13 | public @interface DataPermissionTable { 14 | String[] tables() default {}; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/annotation/UnDataPermissionTable.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | /** 8 | * 加在其他方法上可以使用AOP做切面处理 9 | * 排除对某些表的数据权限处理 10 | */ 11 | @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) 12 | @Target({ElementType.METHOD, ElementType.TYPE}) 13 | public @interface UnDataPermissionTable { 14 | String[] tables() default {}; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/aspect/DataPermissionAspect.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.aspect; 2 | 3 | import com.chj.common.Interface.DataFilterInterface; 4 | import com.chj.common.mybatis.DPHelper; 5 | import com.chj.common.mybatis.DataPermission; 6 | import com.chj.common.mybatis.annotation.DataPermissionAop; 7 | import org.aspectj.lang.ProceedingJoinPoint; 8 | import org.aspectj.lang.annotation.Around; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.aspectj.lang.reflect.MethodSignature; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | /**************************************************************************************** 18 | 实现AOP的切面主要有以下几个要素: 19 | 20 | 使用@Aspect注解将一个java类定义为切面类 21 | 使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。 22 | 根据需要在切入点不同位置的切入内容 23 | 使用@Before在切入点开始处切入内容 24 | 使用@After在切入点结尾处切入内容 25 | 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) 26 | 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容 27 | 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑 28 | 使用@Order(i)注解来标识切面的优先级。i的值越小,优先级越高 29 | * 30 | * @author chenhj 31 | * 32 | * @version 0.0.1 33 | * 34 | * @date 2018/10/19 35 | * 36 | */ 37 | @Aspect 38 | @Order(1) 39 | @Component 40 | public class DataPermissionAspect { 41 | 42 | @Autowired 43 | private DataFilterInterface dataFilterInterface; 44 | /** 45 | * 方法的注解覆盖class注解 46 | * */ 47 | @Around("@annotation(com.chj.common.mybatis.annotation.DataPermissionAop)") 48 | public Object dataPermission(ProceedingJoinPoint point) throws Throwable { 49 | 50 | Class className = point.getTarget().getClass(); 51 | boolean enabled = false; 52 | if (className.isAnnotationPresent(DataPermissionAop.class)) { 53 | DataPermissionAop ds = className.getAnnotation(DataPermissionAop.class); 54 | enabled = ds.enabled(); 55 | } 56 | // 得到访问的方法对象 57 | MethodSignature signature = (MethodSignature) point.getSignature(); 58 | String methodName = signature.getName(); 59 | Class[] argClass = signature.getParameterTypes(); 60 | Method method = className.getMethod(methodName, argClass); 61 | // 判断是否存在@DataPermissionAop注解 62 | if (method.isAnnotationPresent(DataPermissionAop.class)) { 63 | DataPermissionAop annotation = method.getAnnotation(DataPermissionAop.class); 64 | // 取出注解中所需要做数据权限的表 65 | enabled = annotation.enabled(); 66 | } 67 | if(enabled){ 68 | DataPermission dataPermission=dataFilterInterface.queryDataPermission(); 69 | DPHelper.setLocalDataPermissions(dataPermission); 70 | } 71 | return point.proceed(); 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/aspect/DataPermissionFilterAspect.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.aspect; 2 | 3 | import com.chj.common.mybatis.DPHelper; 4 | import com.chj.common.mybatis.DataPermission; 5 | import com.chj.common.mybatis.annotation.DataPermissionTable; 6 | import com.chj.common.mybatis.annotation.UnDataPermissionTable; 7 | import com.chj.common.sys.SessionUser; 8 | import com.chj.common.sys.TableInfo; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.aspectj.lang.reflect.MethodSignature; 13 | import org.springframework.core.annotation.Order; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | /**************************************************************************************** 22 | 实现AOP的切面主要有以下几个要素: 23 | 24 | 使用@Aspect注解将一个java类定义为切面类 25 | 使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。 26 | 根据需要在切入点不同位置的切入内容 27 | 使用@Before在切入点开始处切入内容 28 | 使用@After在切入点结尾处切入内容 29 | 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) 30 | 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容 31 | 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑 32 | 使用@Order(i)注解来标识切面的优先级。i的值越小,优先级越高 33 | * 34 | * @author chenhj 35 | * 36 | * @version 0.0.1 37 | * 38 | * @date 2018/10/19 39 | * 40 | */ 41 | @Aspect 42 | @Order(2) 43 | @Component 44 | public class DataPermissionFilterAspect { 45 | 46 | @Around("@annotation(com.chj.common.mybatis.annotation.DataPermissionTable)") 47 | public Object dataPermissionFilter(ProceedingJoinPoint point) throws Throwable { 48 | Class className = point.getTarget().getClass(); 49 | String[] tableNames = {}; 50 | if (className.isAnnotationPresent(DataPermissionTable.class)) { 51 | DataPermissionTable ds = className.getAnnotation(DataPermissionTable.class); 52 | tableNames = ds.tables(); 53 | }else { 54 | // 得到访问的方法对象 55 | MethodSignature signature = (MethodSignature) point.getSignature(); 56 | String methodName = signature.getName(); 57 | Class[] argClass = signature.getParameterTypes(); 58 | Method method = className.getMethod(methodName, argClass); 59 | // 判断是否存在@DataPermissionAop注解 60 | if (method.isAnnotationPresent(DataPermissionTable.class)) { 61 | DataPermissionTable annotation = method.getAnnotation(DataPermissionTable.class); 62 | // 取出注解中所需要做数据权限的表 63 | tableNames = annotation.tables(); 64 | } 65 | } 66 | SessionUser sessionUser=null; 67 | final String[] tableNamex=tableNames; 68 | if(sessionUser!=null){ 69 | DataPermission dataPermission1 = new DataPermission(); 70 | List tableInfos=new ArrayList<>(); 71 | 72 | DataPermission dataPermission=sessionUser.getDataPermission(); 73 | DPHelper.setLocalDataPermissions(dataPermission); 74 | dataPermission.getTables().forEach(dataPermission2->{ 75 | if(Arrays.asList(tableNamex).contains(dataPermission2.getTableName())){ 76 | tableInfos.add(dataPermission2); 77 | } 78 | }); 79 | dataPermission1.setTables(tableInfos); 80 | DPHelper.start(tmp->dataPermission1,tableNames); 81 | } 82 | return point.proceed(); 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/aspect/DataUnPermissionFilterAspect.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.aspect; 2 | 3 | import com.chj.common.mybatis.DPHelper; 4 | import com.chj.common.mybatis.DataPermission; 5 | import com.chj.common.mybatis.annotation.DataPermissionTable; 6 | import com.chj.common.mybatis.annotation.UnDataPermissionTable; 7 | import com.chj.common.sys.SessionUser; 8 | import com.chj.common.sys.TableInfo; 9 | import org.aspectj.lang.ProceedingJoinPoint; 10 | import org.aspectj.lang.annotation.Around; 11 | import org.aspectj.lang.annotation.Aspect; 12 | import org.aspectj.lang.reflect.MethodSignature; 13 | import org.springframework.core.annotation.Order; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | /**************************************************************************************** 22 | 实现AOP的切面主要有以下几个要素: 23 | 24 | 使用@Aspect注解将一个java类定义为切面类 25 | 使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。 26 | 根据需要在切入点不同位置的切入内容 27 | 使用@Before在切入点开始处切入内容 28 | 使用@After在切入点结尾处切入内容 29 | 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) 30 | 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容 31 | 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑 32 | 使用@Order(i)注解来标识切面的优先级。i的值越小,优先级越高 33 | * 34 | * @author chenhj 35 | * 36 | * @version 0.0.1 37 | * 38 | * @date 2018/10/19 39 | * 40 | */ 41 | @Aspect 42 | @Order(3) 43 | @Component 44 | public class DataUnPermissionFilterAspect { 45 | 46 | @Around("@annotation(com.chj.common.mybatis.annotation.UnDataPermissionTable)") 47 | public Object DataUnPermissionFilter(ProceedingJoinPoint point) throws Throwable { 48 | Class className = point.getTarget().getClass(); 49 | String[] tableNames = {}; 50 | if (className.isAnnotationPresent(UnDataPermissionTable.class)) { 51 | UnDataPermissionTable ds = className.getAnnotation(UnDataPermissionTable.class); 52 | tableNames = ds.tables(); 53 | }else { 54 | // 得到访问的方法对象 55 | MethodSignature signature = (MethodSignature) point.getSignature(); 56 | String methodName = signature.getName(); 57 | Class[] argClass = signature.getParameterTypes(); 58 | Method method = className.getMethod(methodName, argClass); 59 | // 判断是否存在@DataPermissionAop注解 60 | if (method.isAnnotationPresent(UnDataPermissionTable.class)) { 61 | UnDataPermissionTable annotation = method.getAnnotation(UnDataPermissionTable.class); 62 | // 取出注解中所需要做数据权限的表 63 | tableNames = annotation.tables(); 64 | } 65 | } 66 | SessionUser sessionUser=null; 67 | final String[] tableNamex=tableNames; 68 | if(sessionUser!=null){ 69 | DataPermission dataPermission1 = new DataPermission(); 70 | List tableInfos=new ArrayList<>(); 71 | 72 | DataPermission dataPermission=sessionUser.getDataPermission(); 73 | DPHelper.setLocalDataPermissions(dataPermission); 74 | dataPermission.getTables().forEach(dataPermission2->{ 75 | if(!Arrays.asList(tableNamex).contains(dataPermission2.getTableName())){ 76 | tableInfos.add(dataPermission2); 77 | } 78 | }); 79 | dataPermission1.setTables(tableInfos); 80 | DPHelper.start(tmp->dataPermission1,tableNames); 81 | } 82 | return point.proceed(); 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/interceptor/DataPermissionInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.interceptor; 2 | 3 | import com.chj.common.mybatis.DPHelper; 4 | import com.chj.common.mybatis.DataPermission; 5 | import com.chj.common.mybatis.visitor.SelectVisitorImpl; 6 | import com.chj.common.utils.ReflectUtil; 7 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; 8 | import net.sf.jsqlparser.statement.select.Select; 9 | import org.apache.ibatis.executor.statement.RoutingStatementHandler; 10 | import org.apache.ibatis.executor.statement.StatementHandler; 11 | import org.apache.ibatis.mapping.BoundSql; 12 | import org.apache.ibatis.plugin.*; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.core.annotation.Order; 16 | import org.springframework.stereotype.Component; 17 | 18 | import java.sql.Connection; 19 | import java.util.Properties; 20 | 21 | /** 22 | * @author chenhj 23 | * @description 数据权限拦截器(在PageInterceptor之后) 24 | * @create 2018-12-24 下午3:12 25 | */ 26 | @Intercepts( 27 | { 28 | @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) 29 | } 30 | ) 31 | @Order(1) 32 | @Component 33 | public class DataPermissionInterceptor implements Interceptor { 34 | private final static Logger logger = LoggerFactory.getLogger(DataPermissionInterceptor.class); 35 | @Override 36 | public Object intercept(Invocation invocation) throws Throwable { 37 | RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget(); 38 | StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate"); 39 | //从当前线程获取需要进行数据权限控制的业务 40 | DataPermission dataPermission = DPHelper.getLocalDataPermissions(); 41 | //判断有没有进行数据权限控制,是不是最高权限的管理员(这里指的是数据权限的白名单用户) 42 | if (dataPermission != null && dataPermission.getAdmin() == false && !dataPermission.getTables().isEmpty()) { 43 | BoundSql boundSql = delegate.getBoundSql(); 44 | String sql = boundSql.getSql(); 45 | if(CCJSqlParserUtil.parse(sql) instanceof Select){ 46 | //获得方法类型 47 | Select select = (Select) CCJSqlParserUtil.parse(sql); 48 | select.getSelectBody().accept(new SelectVisitorImpl()); 49 | //修改sql 50 | ReflectUtil.setFieldValue(boundSql, "sql", select.toString()); 51 | } 52 | } 53 | return invocation.proceed(); 54 | } 55 | 56 | @Override 57 | public Object plugin(Object target) { 58 | return Plugin.wrap(target, this); 59 | } 60 | 61 | @Override 62 | public void setProperties(Properties properties) { 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/interceptor/ParamInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.interceptor; 2 | import org.apache.commons.lang.StringUtils; 3 | import org.apache.ibatis.binding.MapperMethod; 4 | import org.apache.ibatis.executor.Executor; 5 | import org.apache.ibatis.executor.parameter.ParameterHandler; 6 | import org.apache.ibatis.mapping.BoundSql; 7 | import org.apache.ibatis.mapping.MappedStatement; 8 | import org.apache.ibatis.mapping.ParameterMapping; 9 | import org.apache.ibatis.mapping.SqlCommandType; 10 | import org.apache.ibatis.plugin.*; 11 | import org.apache.ibatis.session.RowBounds; 12 | import org.springframework.beans.BeanUtils; 13 | import org.springframework.util.ClassUtils; 14 | 15 | import java.beans.PropertyDescriptor; 16 | import java.lang.reflect.Field; 17 | import java.lang.reflect.InvocationTargetException; 18 | import java.sql.PreparedStatement; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | 24 | /** 25 | * @author chenhj 26 | * @description 参数拦截器 27 | * @create 2018-12-24 下午3:12 28 | */ 29 | @Intercepts({ 30 | @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class), 31 | @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), 32 | @Signature(type = Executor.class, method = "createCacheKey", args = {MappedStatement.class, Object.class, RowBounds.class, BoundSql.class}) 33 | } 34 | ) 35 | public class ParamInterceptor implements Interceptor { 36 | 37 | private static final String WAREHOUSE_ID = "warehouseId"; 38 | 39 | @Override 40 | public Object intercept(Invocation invocation) throws Throwable { 41 | 42 | // 拦截 Executor 的 update 方法 生成sql前将 warehouseId 设置到实体中 43 | if (invocation.getTarget() instanceof Executor && invocation.getArgs().length == 2) { 44 | return invokeUpdate(invocation); 45 | } 46 | // 拦截 Executor 的 createCacheKey 方法,pageHelper插件会拦截 query 方法,调用此方法,提前将参数设置到参数集合中 47 | if (invocation.getTarget() instanceof Executor && invocation.getArgs().length == 4) { 48 | return invokeCacheKey(invocation); 49 | } 50 | //拦截 ParameterHandler 的 setParameters 方法 动态设置参数 51 | if (invocation.getTarget() instanceof ParameterHandler) { 52 | return invokeSetParameter(invocation); 53 | } 54 | return null; 55 | } 56 | 57 | private Object invokeCacheKey(Invocation invocation) throws Exception { 58 | Executor executor = (Executor) invocation.getTarget(); 59 | MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; 60 | if (ms.getSqlCommandType() != SqlCommandType.SELECT) { 61 | return invocation.proceed(); 62 | } 63 | 64 | Object parameterObject = invocation.getArgs()[1]; 65 | RowBounds rowBounds = (RowBounds) invocation.getArgs()[2]; 66 | BoundSql boundSql = (BoundSql) invocation.getArgs()[3]; 67 | 68 | List paramNames = new ArrayList<>(); 69 | boolean hasKey = hasParamKey(paramNames, boundSql.getParameterMappings()); 70 | 71 | if (!hasKey) { 72 | return invocation.proceed(); 73 | } 74 | // 改写参数 75 | parameterObject = processSingle(parameterObject, paramNames); 76 | 77 | return executor.createCacheKey(ms, parameterObject, rowBounds, boundSql); 78 | } 79 | 80 | private Object invokeUpdate(Invocation invocation) throws Exception { 81 | Executor executor = (Executor) invocation.getTarget(); 82 | // 获取第一个参数 83 | MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; 84 | // 非 insert 语句 不处理 85 | if (ms.getSqlCommandType() != SqlCommandType.INSERT) { 86 | return invocation.proceed(); 87 | } 88 | // mybatis的参数对象 89 | Object paramObj = invocation.getArgs()[1]; 90 | if (paramObj == null) { 91 | return invocation.proceed(); 92 | } 93 | 94 | // 插入语句只传一个基本类型参数, 不做处理 95 | if (ClassUtils.isPrimitiveOrWrapper(paramObj.getClass()) 96 | || String.class.isAssignableFrom(paramObj.getClass()) 97 | || Number.class.isAssignableFrom(paramObj.getClass())) { 98 | return invocation.proceed(); 99 | } 100 | 101 | processParam(paramObj); 102 | return executor.update(ms, paramObj); 103 | } 104 | 105 | 106 | private Object invokeSetParameter(Invocation invocation) throws Exception { 107 | 108 | ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); 109 | PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0]; 110 | 111 | // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射 112 | Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql"); 113 | boundSqlField.setAccessible(true); 114 | BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler); 115 | 116 | List paramNames = new ArrayList<>(); 117 | // 若参数映射没有包含的key直接返回 118 | boolean hasKey = hasParamKey(paramNames, boundSql.getParameterMappings()); 119 | if (!hasKey) { 120 | return invocation.proceed(); 121 | } 122 | 123 | // 反射获取 参数对像 124 | Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); 125 | parameterField.setAccessible(true); 126 | Object parameterObject = parameterField.get(parameterHandler); 127 | 128 | // 改写参数 129 | parameterObject = processSingle(parameterObject, paramNames); 130 | 131 | // 改写的参数设置到原parameterHandler对象 132 | parameterField.set(parameterHandler, parameterObject); 133 | parameterHandler.setParameters(ps); 134 | return null; 135 | } 136 | 137 | // 判断已生成sql参数映射中是否包含warehouseId 138 | private boolean hasParamKey(List paramNames, List parameterMappings) { 139 | boolean hasKey = false; 140 | for (ParameterMapping parameterMapping : parameterMappings) { 141 | if (StringUtils.equals(parameterMapping.getProperty(), WAREHOUSE_ID)) { 142 | hasKey = true; 143 | } else { 144 | paramNames.add(parameterMapping.getProperty()); 145 | } 146 | } 147 | return hasKey; 148 | } 149 | 150 | private Object processSingle(Object paramObj, List paramNames) throws Exception { 151 | 152 | Map paramMap = new MapperMethod.ParamMap<>(); 153 | if (paramObj == null) { 154 | paramMap.put(WAREHOUSE_ID, 1L); 155 | paramObj = paramMap; 156 | // 单参数 将 参数转为 map 157 | } else if (ClassUtils.isPrimitiveOrWrapper(paramObj.getClass()) 158 | || String.class.isAssignableFrom(paramObj.getClass()) 159 | || Number.class.isAssignableFrom(paramObj.getClass())) { 160 | if (paramNames.size() == 1) { 161 | paramMap.put(paramNames.iterator().next(), paramObj); 162 | paramMap.put(WAREHOUSE_ID, 1L); 163 | paramObj = paramMap; 164 | } 165 | } else { 166 | processParam(paramObj); 167 | } 168 | 169 | return paramObj; 170 | } 171 | 172 | private void processParam(Object parameterObject) throws IllegalAccessException, InvocationTargetException { 173 | // 处理参数对象 如果是 map 且map的key 中没有 warehouseId,添加到参数map中 174 | // 如果参数是bean,反射设置值 175 | if (parameterObject instanceof Map) { 176 | ((Map) parameterObject).putIfAbsent(WAREHOUSE_ID, 1L); 177 | } else { 178 | PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(parameterObject.getClass(), WAREHOUSE_ID); 179 | if (ps != null && ps.getReadMethod() != null && ps.getWriteMethod() != null) { 180 | Object value = ps.getReadMethod().invoke(parameterObject); 181 | if (value == null) { 182 | ps.getWriteMethod().invoke(parameterObject, 1L); 183 | } 184 | } 185 | } 186 | } 187 | 188 | @Override 189 | public Object plugin(Object target) { 190 | return Plugin.wrap(target, this); 191 | } 192 | 193 | @Override 194 | public void setProperties(Properties properties) { 195 | 196 | } 197 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/interceptor/ResultInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.interceptor; 2 | 3 | import com.chj.common.utils.ReflectUtil; 4 | import org.apache.ibatis.executor.resultset.ResultSetHandler; 5 | import org.apache.ibatis.mapping.MappedStatement; 6 | import org.apache.ibatis.plugin.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.sql.Statement; 12 | import java.util.Properties; 13 | 14 | /** 15 | * mybatis数据权限拦截器 - handleResultSets 16 | * 对结果集进行过滤(预留 字段显示过滤) 17 | * @author chenhj 18 | */ 19 | @Intercepts({ 20 | @Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class}) 21 | }) 22 | @Component 23 | public class ResultInterceptor implements Interceptor { 24 | /** 日志 */ 25 | private static final Logger log = LoggerFactory.getLogger(ResultInterceptor.class); 26 | 27 | @Override 28 | public Object plugin(Object target) { 29 | return Plugin.wrap(target, this); 30 | } 31 | 32 | @Override 33 | public void setProperties(Properties properties) {} 34 | 35 | @Override 36 | public Object intercept(Invocation invocation) throws Throwable { 37 | if(log.isInfoEnabled()){ 38 | log.debug("进入 ResultInterceptor 拦截器..."); 39 | } 40 | 41 | //DefaultResultSetHandler handler = (DefaultResultSetHandler) invocation.getTarget(); 42 | //ParameterHandler parameterHandler = (ParameterHandler)ReflectUtil.getFieldValue(handler, "parameterHandler"); 43 | //Object parameterObj = parameterHandler.getParameterObject(); 44 | 45 | ResultSetHandler resultSetHandler1 = (ResultSetHandler) invocation.getTarget(); 46 | //通过java反射获得mappedStatement属性值 47 | //可以获得mybatis里的resultype 48 | MappedStatement mappedStatement = (MappedStatement)ReflectUtil.getFieldValue(resultSetHandler1, "mappedStatement"); 49 | //获取切面对象 50 | // PermissionAop permissionAop = PermissionUtils.getPermissionByDelegate(mappedStatement); 51 | 52 | //执行请求方法,并将所得结果保存到result中 53 | Object result = invocation.proceed(); 54 | /*if(permissionAop != null) { 55 | if (result instanceof ArrayList) { 56 | ArrayList resultList = (ArrayList) result; 57 | for (int i = 0; i < resultList.size(); i++) { 58 | Object oi = resultList.get(i); 59 | Class c = oi.getClass(); 60 | Class[] types = {String.class}; 61 | Method method = c.getMethod("set"+StringUtils.capitalize(""), types); 62 | // 调用obj对象的 method 方法 63 | method.invoke(oi, ""); 64 | if(log.isInfoEnabled()){ 65 | log.info("数据权限处理【过滤结果】..."); 66 | } 67 | } 68 | } 69 | }*/ 70 | return result; 71 | } 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/visitor/ExpressionVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.visitor; 2 | 3 | 4 | import net.sf.jsqlparser.expression.*; 5 | import net.sf.jsqlparser.expression.operators.arithmetic.*; 6 | import net.sf.jsqlparser.expression.operators.conditional.AndExpression; 7 | import net.sf.jsqlparser.expression.operators.conditional.OrExpression; 8 | import net.sf.jsqlparser.expression.operators.relational.*; 9 | import net.sf.jsqlparser.schema.Column; 10 | import net.sf.jsqlparser.statement.select.SubSelect; 11 | import net.sf.jsqlparser.statement.select.WithItem; 12 | 13 | public class ExpressionVisitorImpl implements ExpressionVisitor { 14 | 15 | // 单表达式 16 | @Override 17 | public void visit(SignedExpression signedExpression) { 18 | signedExpression.accept(new ExpressionVisitorImpl()); 19 | } 20 | 21 | // jdbc参数 22 | @Override 23 | public void visit(JdbcParameter jdbcParameter) { 24 | } 25 | 26 | // jdbc参数 27 | @Override 28 | public void visit(JdbcNamedParameter jdbcNamedParameter) { 29 | } 30 | 31 | // 32 | @Override 33 | public void visit(Parenthesis parenthesis) { 34 | parenthesis.getExpression().accept(new ExpressionVisitorImpl()); 35 | } 36 | 37 | // between 38 | @Override 39 | public void visit(Between between) { 40 | between.getLeftExpression().accept(new ExpressionVisitorImpl()); 41 | between.getBetweenExpressionStart().accept(new ExpressionVisitorImpl()); 42 | between.getBetweenExpressionEnd().accept(new ExpressionVisitorImpl()); 43 | } 44 | 45 | // in表达式 46 | @Override 47 | public void visit(InExpression inExpression) { 48 | if (inExpression.getLeftExpression() != null) { 49 | inExpression.getLeftExpression() 50 | .accept(new ExpressionVisitorImpl()); 51 | } else if (inExpression.getLeftItemsList() != null) { 52 | inExpression.getLeftItemsList().accept(new ItemsListVisitorImpl()); 53 | } 54 | inExpression.getRightItemsList().accept(new ItemsListVisitorImpl()); 55 | } 56 | 57 | // 子查询 58 | @Override 59 | public void visit(SubSelect subSelect) { 60 | if (subSelect.getWithItemsList() != null) { 61 | for (WithItem withItem : subSelect.getWithItemsList()) { 62 | withItem.accept(new SelectVisitorImpl()); 63 | } 64 | } 65 | subSelect.getSelectBody().accept(new SelectVisitorImpl()); 66 | } 67 | 68 | // exist 69 | @Override 70 | public void visit(ExistsExpression existsExpression) { 71 | existsExpression.getRightExpression().accept( 72 | new ExpressionVisitorImpl()); 73 | } 74 | 75 | // allComparisonExpression?? 76 | @Override 77 | public void visit(AllComparisonExpression allComparisonExpression) { 78 | allComparisonExpression.getSubSelect().getSelectBody() 79 | .accept(new SelectVisitorImpl()); 80 | } 81 | 82 | // anyComparisonExpression?? 83 | @Override 84 | public void visit(AnyComparisonExpression anyComparisonExpression) { 85 | anyComparisonExpression.getSubSelect().getSelectBody() 86 | .accept(new SelectVisitorImpl()); 87 | } 88 | 89 | // oexpr?? 90 | @Override 91 | public void visit(OracleHierarchicalExpression oexpr) { 92 | if (oexpr.getStartExpression() != null) { 93 | oexpr.getStartExpression().accept(this); 94 | } 95 | 96 | if (oexpr.getConnectExpression() != null) { 97 | oexpr.getConnectExpression().accept(this); 98 | } 99 | } 100 | 101 | // rowConstructor? 102 | @Override 103 | public void visit(RowConstructor rowConstructor) { 104 | for (Expression expr : rowConstructor.getExprList().getExpressions()) { 105 | expr.accept(this); 106 | } 107 | } 108 | 109 | // cast 110 | @Override 111 | public void visit(CastExpression cast) { 112 | cast.getLeftExpression().accept(new ExpressionVisitorImpl()); 113 | } 114 | 115 | // 加法 116 | @Override 117 | public void visit(Addition addition) { 118 | visitBinaryExpression(addition); 119 | } 120 | 121 | // 除法 122 | @Override 123 | public void visit(Division division) { 124 | visitBinaryExpression(division); 125 | } 126 | 127 | // 乘法 128 | @Override 129 | public void visit(Multiplication multiplication) { 130 | visitBinaryExpression(multiplication); 131 | } 132 | 133 | // 减法 134 | @Override 135 | public void visit(Subtraction subtraction) { 136 | visitBinaryExpression(subtraction); 137 | } 138 | 139 | // and表达式 140 | @Override 141 | public void visit(AndExpression andExpression) { 142 | visitBinaryExpression(andExpression); 143 | } 144 | 145 | // or表达式 146 | @Override 147 | public void visit(OrExpression orExpression) { 148 | visitBinaryExpression(orExpression); 149 | } 150 | 151 | // 等式 152 | @Override 153 | public void visit(EqualsTo equalsTo) { 154 | visitBinaryExpression(equalsTo); 155 | } 156 | 157 | // 大于 158 | @Override 159 | public void visit(GreaterThan greaterThan) { 160 | visitBinaryExpression(greaterThan); 161 | } 162 | 163 | // 大于等于 164 | @Override 165 | public void visit(GreaterThanEquals greaterThanEquals) { 166 | visitBinaryExpression(greaterThanEquals); 167 | } 168 | 169 | // like表达式 170 | @Override 171 | public void visit(LikeExpression likeExpression) { 172 | visitBinaryExpression(likeExpression); 173 | } 174 | 175 | // 小于 176 | @Override 177 | public void visit(MinorThan minorThan) { 178 | visitBinaryExpression(minorThan); 179 | } 180 | 181 | // 小于等于 182 | @Override 183 | public void visit(MinorThanEquals minorThanEquals) { 184 | visitBinaryExpression(minorThanEquals); 185 | } 186 | 187 | // 不等于 188 | @Override 189 | public void visit(NotEqualsTo notEqualsTo) { 190 | visitBinaryExpression(notEqualsTo); 191 | } 192 | 193 | // concat 194 | @Override 195 | public void visit(Concat concat) { 196 | visitBinaryExpression(concat); 197 | } 198 | 199 | // matches? 200 | @Override 201 | public void visit(Matches matches) { 202 | visitBinaryExpression(matches); 203 | } 204 | 205 | // bitwiseAnd位运算? 206 | @Override 207 | public void visit(BitwiseAnd bitwiseAnd) { 208 | visitBinaryExpression(bitwiseAnd); 209 | } 210 | 211 | // bitwiseOr? 212 | @Override 213 | public void visit(BitwiseOr bitwiseOr) { 214 | visitBinaryExpression(bitwiseOr); 215 | } 216 | 217 | // bitwiseXor? 218 | @Override 219 | public void visit(BitwiseXor bitwiseXor) { 220 | visitBinaryExpression(bitwiseXor); 221 | } 222 | 223 | // 取模运算modulo? 224 | @Override 225 | public void visit(Modulo modulo) { 226 | visitBinaryExpression(modulo); 227 | } 228 | 229 | // rexp?? 230 | @Override 231 | public void visit(RegExpMatchOperator rexpr) { 232 | visitBinaryExpression(rexpr); 233 | } 234 | 235 | // regExpMySQLOperator?? 236 | @Override 237 | public void visit(RegExpMySQLOperator regExpMySQLOperator) { 238 | visitBinaryExpression(regExpMySQLOperator); 239 | } 240 | 241 | // 二元表达式 242 | public void visitBinaryExpression(BinaryExpression binaryExpression) { 243 | binaryExpression.getLeftExpression() 244 | .accept(new ExpressionVisitorImpl()); 245 | binaryExpression.getRightExpression().accept( 246 | new ExpressionVisitorImpl()); 247 | } 248 | 249 | // -------------------------下面都是没用到的----------------------------------- 250 | 251 | // aexpr?? 252 | @Override 253 | public void visit(AnalyticExpression aexpr) { 254 | } 255 | 256 | // wgexpr?? 257 | @Override 258 | public void visit(WithinGroupExpression wgexpr) { 259 | } 260 | 261 | // eexpr?? 262 | @Override 263 | public void visit(ExtractExpression eexpr) { 264 | } 265 | 266 | // iexpr?? 267 | @Override 268 | public void visit(IntervalExpression iexpr) { 269 | } 270 | 271 | // jsonExpr?? 272 | @Override 273 | public void visit(JsonExpression jsonExpr) { 274 | } 275 | 276 | @Override 277 | public void visit(JsonOperator jsonExpr) { 278 | 279 | } 280 | 281 | // hint? 282 | @Override 283 | public void visit(OracleHint hint) { 284 | } 285 | 286 | // timeKeyExpression? 287 | @Override 288 | public void visit(TimeKeyExpression timeKeyExpression) { 289 | } 290 | 291 | // caseExpression? 292 | @Override 293 | public void visit(CaseExpression caseExpression) { 294 | } 295 | 296 | // when? 297 | @Override 298 | public void visit(WhenClause whenClause) { 299 | } 300 | 301 | // var?? 302 | @Override 303 | public void visit(UserVariable var) { 304 | } 305 | 306 | // bind? 307 | @Override 308 | public void visit(NumericBind bind) { 309 | } 310 | 311 | // aexpr? 312 | @Override 313 | public void visit(KeepExpression aexpr) { 314 | } 315 | 316 | // groupConcat? 317 | @Override 318 | public void visit(MySQLGroupConcat groupConcat) { 319 | } 320 | 321 | // table列 322 | @Override 323 | public void visit(Column tableColumn) { 324 | } 325 | 326 | // double类型值 327 | @Override 328 | public void visit(DoubleValue doubleValue) { 329 | } 330 | 331 | // long类型值 332 | @Override 333 | public void visit(LongValue longValue) { 334 | } 335 | 336 | // 16进制类型值 337 | @Override 338 | public void visit(HexValue hexValue) { 339 | } 340 | 341 | // date类型值 342 | @Override 343 | public void visit(DateValue dateValue) { 344 | } 345 | 346 | // time类型值 347 | @Override 348 | public void visit(TimeValue timeValue) { 349 | } 350 | 351 | // 时间戳类型值 352 | @Override 353 | public void visit(TimestampValue timestampValue) { 354 | } 355 | 356 | // 空值 357 | @Override 358 | public void visit(NullValue nullValue) { 359 | } 360 | 361 | // 方法 362 | @Override 363 | public void visit(Function function) { 364 | } 365 | 366 | // 字符串类型值 367 | @Override 368 | public void visit(StringValue stringValue) { 369 | } 370 | 371 | // is null表达式 372 | @Override 373 | public void visit(IsNullExpression isNullExpression) { 374 | } 375 | 376 | // literal? 377 | @Override 378 | public void visit(DateTimeLiteralExpression literal) { 379 | } 380 | 381 | @Override 382 | public void visit(NotExpression aThis) { 383 | 384 | } 385 | } 386 | 387 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/visitor/FromItemVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.visitor; 2 | 3 | import com.chj.common.mybatis.DPHelper; 4 | import com.chj.common.sys.ColumnInfo; 5 | import com.chj.common.sys.TableInfo; 6 | import com.chj.common.utils.SqlSpliceUtils; 7 | import net.sf.jsqlparser.JSQLParserException; 8 | import net.sf.jsqlparser.expression.Alias; 9 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; 10 | import net.sf.jsqlparser.schema.Table; 11 | import net.sf.jsqlparser.statement.select.*; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.List; 16 | import java.util.UUID; 17 | 18 | /** 19 | * @author: chenhj 20 | * @date: 18-5-7 下午2:49 21 | * @desciptions: 22 | */ 23 | public class FromItemVisitorImpl implements FromItemVisitor { 24 | private Logger logger = LoggerFactory.getLogger(FromItemVisitorImpl.class); 25 | 26 | private SubSelect subSelect; 27 | 28 | @Override 29 | public void visit(Table table) { 30 | String tableName = table.getName(); 31 | //关键点:解析到需要进行数据权限控制的表时进行拼装,可以从当前线程获取表数据 32 | //需要进行的数据权限控制的表数据 33 | List tables = DPHelper.getLocalDataPermissions().getTables(); 34 | tables.forEach(tableInfo -> { 35 | if (tableInfo.getTableName().equalsIgnoreCase(tableName)) { 36 | List columnInfoList = tableInfo.getColumnInfoList(); 37 | 38 | SubSelect subSelect = new SubSelect(); 39 | String subSql = SqlSpliceUtils.spliceColumnInfos(tableName, columnInfoList); 40 | try { 41 | subSelect.setSelectBody(((Select) (CCJSqlParserUtil.parse(subSql))).getSelectBody()); 42 | } catch (JSQLParserException e) { 43 | logger.error("数据权限sql解析异常"); 44 | } 45 | //TODO:采用随机别名不能避免重名 46 | subSelect.setAlias(table.getAlias() != null ? table.getAlias() : new Alias("DP" + UUID.randomUUID() 47 | .toString().replace("-", ""))); 48 | this.subSelect = subSelect; 49 | } 50 | }); 51 | } 52 | 53 | public SubSelect getSubSelect() { 54 | return this.subSelect; 55 | } 56 | 57 | // FROM 子查询 58 | @Override 59 | public void visit(SubSelect subSelect) { 60 | // 如果是子查询的话返回到select接口实现类 61 | subSelect.getSelectBody().accept(new SelectVisitorImpl()); 62 | } 63 | 64 | // FROM subjoin 65 | @Override 66 | public void visit(SubJoin subjoin) { 67 | subjoin.getLeft().accept(new FromItemVisitorImpl()); 68 | subjoin.getJoin().getRightItem().accept(new FromItemVisitorImpl()); 69 | } 70 | 71 | // FROM 横向子查询 72 | @Override 73 | public void visit(LateralSubSelect lateralSubSelect) { 74 | lateralSubSelect.getSubSelect().getSelectBody() 75 | .accept(new SelectVisitorImpl()); 76 | } 77 | 78 | // FROM value列表 79 | @Override 80 | public void visit(ValuesList valuesList) { 81 | } 82 | 83 | // FROM tableFunction 84 | @Override 85 | public void visit(TableFunction tableFunction) { 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/visitor/ItemsListVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.visitor; 2 | 3 | import net.sf.jsqlparser.expression.Expression; 4 | import net.sf.jsqlparser.expression.operators.relational.ExpressionList; 5 | import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; 6 | import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; 7 | import net.sf.jsqlparser.statement.select.SubSelect; 8 | 9 | import java.util.List; 10 | 11 | public class ItemsListVisitorImpl implements ItemsListVisitor { 12 | 13 | @Override 14 | public void visit(SubSelect ss) { 15 | ss.getSelectBody().accept(new SelectVisitorImpl()); 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | @Override 20 | public void visit(ExpressionList el) { 21 | List list = el.getExpressions(); 22 | if (list != null && list.size() > 0) { 23 | for (Expression expr : list) { 24 | expr.accept(new ExpressionVisitorImpl()); 25 | } 26 | } 27 | } 28 | 29 | @Override 30 | public void visit(MultiExpressionList multiExprList) { 31 | 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/visitor/SelectItemVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.visitor; 2 | 3 | import net.sf.jsqlparser.statement.select.AllColumns; 4 | import net.sf.jsqlparser.statement.select.AllTableColumns; 5 | import net.sf.jsqlparser.statement.select.SelectExpressionItem; 6 | import net.sf.jsqlparser.statement.select.SelectItemVisitor; 7 | 8 | public class SelectItemVisitorImpl implements SelectItemVisitor { 9 | 10 | @Override 11 | public void visit(AllColumns allColumns) { 12 | } 13 | 14 | @Override 15 | public void visit(AllTableColumns allTableColumns) { 16 | } 17 | 18 | @Override 19 | public void visit(SelectExpressionItem selectExpressionItem) { 20 | selectExpressionItem.getExpression().accept(new ExpressionVisitorImpl()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/mybatis/visitor/SelectVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.mybatis.visitor; 2 | 3 | import com.chj.common.mybatis.DPHelper; 4 | import net.sf.jsqlparser.statement.select.*; 5 | 6 | public class SelectVisitorImpl implements SelectVisitor { 7 | 8 | // 主要工作就是实现各种底层visitor,然后在解析的时候添加条件 9 | // 正常的select,也就是包含全部属性的select 10 | @Override 11 | public void visit(PlainSelect plainSelect) { 12 | 13 | // 访问 select 14 | if (plainSelect.getSelectItems() != null) { 15 | for (SelectItem item : plainSelect.getSelectItems()) { 16 | item.accept(new SelectItemVisitorImpl()); 17 | } 18 | } 19 | 20 | // 访问from 21 | FromItem fromItem = plainSelect.getFromItem(); 22 | FromItemVisitorImpl fromItemVisitorImpl = new FromItemVisitorImpl(); 23 | fromItem.accept(fromItemVisitorImpl); 24 | 25 | if (fromItemVisitorImpl.getSubSelect() != null) { 26 | plainSelect.setFromItem(fromItemVisitorImpl.getSubSelect()); 27 | if (!DPHelper.getChangeTable()) { 28 | DPHelper.setChangeTable(true); 29 | } 30 | } 31 | 32 | // 访问where 33 | if (plainSelect.getWhere() != null) { 34 | plainSelect.getWhere().accept(new ExpressionVisitorImpl()); 35 | } 36 | 37 | // 访问join 38 | if (plainSelect.getJoins() != null) { 39 | for (Join join : plainSelect.getJoins()) { 40 | FromItemVisitorImpl fromItemVisitorImplTemp = new FromItemVisitorImpl(); 41 | join.getRightItem().accept(fromItemVisitorImplTemp); 42 | if (fromItemVisitorImplTemp.getSubSelect() != null) { 43 | join.setRightItem(fromItemVisitorImplTemp.getSubSelect()); 44 | if (!DPHelper.getChangeTable()) { 45 | DPHelper.setChangeTable(true); 46 | } 47 | } 48 | } 49 | } 50 | 51 | // 访问 order by 52 | if (plainSelect.getOrderByElements() != null) { 53 | for (OrderByElement orderByElement : plainSelect 54 | .getOrderByElements()) { 55 | orderByElement.getExpression().accept( 56 | new ExpressionVisitorImpl()); 57 | } 58 | } 59 | 60 | // 访问group by having 61 | if (plainSelect.getHaving() != null) { 62 | plainSelect.getHaving().accept(new ExpressionVisitorImpl()); 63 | } 64 | 65 | } 66 | 67 | // set操作列表 68 | @Override 69 | public void visit(SetOperationList setOpList) { 70 | for (SelectBody plainSelect : setOpList.getSelects()) { 71 | plainSelect.accept(new SelectVisitorImpl()); 72 | } 73 | } 74 | 75 | // with项 76 | @Override 77 | public void visit(WithItem withItem) { 78 | withItem.getSelectBody().accept(new SelectVisitorImpl()); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/registrar/DataPermissionRegistrar.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.registrar; 2 | 3 | import com.chj.common.mybatis.DataPermission; 4 | import com.chj.common.mybatis.annotation.DataPermissionAop; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | /** 8 | * @author chenhj 9 | */ 10 | public class DataPermissionRegistrar { 11 | 12 | 13 | @SuppressWarnings("rawtypes") 14 | @Bean 15 | public DataPermission cacheManager() { 16 | DataPermission dataPermission = new DataPermission(); 17 | return dataPermission; 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com.chj.common/sys/ClientInfo.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.sys; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * class_name: $ 7 | * package: com.chj.wms.vo$ 8 | * describe: TODO 9 | * creat_user: wanwt@senthinkcom 10 | * creat_date: $ 11 | * creat_time: $ 12 | **/ 13 | @Data 14 | public class ClientInfo { 15 | private String clientId; 16 | private String accessToken; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/sys/ColumnInfo.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.sys; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author: chenhj 7 | * @date: 18-5-7 下午2:31 8 | * @desciptions: 有权限的id和字段,类似与二元组 9 | */ 10 | @Data 11 | public class ColumnInfo { 12 | private String columnName; 13 | 14 | private String columnValue; 15 | /** 16 | * 字段类型 17 | **/ 18 | private String columnType; 19 | /** 20 | * 规则(like in =) 21 | **/ 22 | private String columnRule; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/sys/SessionUser.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.sys; 2 | 3 | import com.chj.common.mybatis.DataPermission; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * @author chenhj 11 | * @since 2018-3-24 12 | */ 13 | @Data 14 | public class SessionUser implements Serializable { 15 | private static final long serialVersionUID = 1L; 16 | //目前userId直接存放处 17 | private Long userId; 18 | private String userCode; 19 | private String userName; 20 | private String userType; 21 | private String language; 22 | private String timeZone; 23 | private String roleCode; 24 | private String tokenId; 25 | private String password; 26 | //用户名称 27 | private String nickName; 28 | private String[] roleId; 29 | private String roleName; 30 | private String isFirstLogin; 31 | /** 32 | * 登录类型0:pc 1:rf*/ 33 | private String loginType; 34 | /** 35 | * 登录人级别*/ 36 | private String loginUserType; 37 | /** 38 | * 是否需要分库校验 非分库情况下为false*/ 39 | private boolean isCheckedObject; 40 | /** 41 | * 分库代号 wms以仓库分库 值为3;待补充*/ 42 | private String objectType="3"; 43 | /** 44 | * 所属系统*/ 45 | private String sys ; 46 | /** 47 | * 分库标示,返回分库ID*/ 48 | private Long shard; 49 | //登录用户的数据权限 50 | private DataPermission dataPermission; 51 | /** 52 | * 各系统预留字段 53 | */ 54 | private Object userInfo; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/sys/TableInfo.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.sys; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | @Data 9 | public class TableInfo implements Serializable { 10 | private static final long serialVersionUID = 1L; 11 | private String tableName; 12 | List columnInfoList; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/utils/DataTypeUtils.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.utils; 2 | 3 | /** 4 | * 数据类型工具类 5 | * @author chenhj 6 | * @date Nov 10, 2018 7 | */ 8 | public class DataTypeUtils { 9 | 10 | public static final String String = "String"; 11 | public static final String Byte = "Byte"; 12 | public static final String ByteArray = "byte[]"; 13 | public static final String Short = "Short"; 14 | public static final String Integer = "Integer"; 15 | public static final String Long = "Long"; 16 | public static final String Float = "Float"; 17 | public static final String Double = "Double"; 18 | public static final String Character = "Character"; 19 | public static final String Boolean = "Boolean"; 20 | public static final String Date = "java.util.Date"; 21 | public static final String Time = "java.sql.Time"; 22 | public static final String Timestamp = "java.sql.Timestamp"; 23 | public static final String Object = "java.lang.Object"; 24 | 25 | public static final String CHAR = "CHAR"; 26 | public static final String TEXT = "TEXT"; 27 | public static final String BLOB = "BLOB"; 28 | public static final String CLOB = "CLOB"; 29 | public static final String BIT = "BIT"; 30 | public static final String TINYINT = "TINYINT"; 31 | public static final String SMALLINT = "SMALLINT"; 32 | public static final String INT = "INT"; 33 | public static final String INT8 = "INT8"; 34 | public static final String BIGINT = "BIGINT"; 35 | public static final String LONG = "LONG"; 36 | public static final String FLOAT = "FLOAT"; 37 | public static final String NUMBER = "NUMBER"; 38 | public static final String NUMERIC = "NUMERIC"; 39 | public static final String DECIMAL = "DECIMAL"; 40 | public static final String DOUBLE = "DOUBLE"; 41 | public static final String BOOL = "BOOL"; 42 | public static final String BOOLEAN = "BOOLEAN"; 43 | public static final String DATE = "DATE"; 44 | public static final String TIME = "TIME"; 45 | public static final String BINARY = "BINARY"; 46 | public static final String VARBINARY = "VARBINARY"; 47 | public static final String CHAR_FOR_BIT_DATA = "CHAR FOR BIT DATA"; 48 | public static final String RAW = "RAW"; 49 | public static final String IMAGE = "IMAGE"; 50 | public static final String BYTE = "BYTE"; 51 | public static final String DK_CM_BLOB = "DK_CM_BLOB"; 52 | public static final String DK_CM_SMALLINT = "DK_CM_SMALLINT"; 53 | public static final String OBJECT = "OBJECT"; 54 | public static final String LONGVARCHAR = "LONGVARCHAR"; 55 | public static final String INTEGER = "INTEGER"; 56 | public static final String TIMESTAMP = "TIMESTAMP"; 57 | public static final String VARCHAR = "VARCHAR"; 58 | 59 | /** 60 | * 根据数据库类型获取对应的JAVA类型 61 | * @param dataType 62 | * @return 63 | */ 64 | public static String getJavaType(String dataType) { 65 | String javaType = String; 66 | if (dataType != null) { 67 | dataType = dataType.toUpperCase(); 68 | if (dataType.contains(BINARY) || dataType.contains(CHAR_FOR_BIT_DATA) || dataType.contains(RAW) 69 | || dataType.contains(IMAGE) || dataType.contains(BYTE) || dataType.contains(DK_CM_BLOB) 70 | || dataType.contains(BLOB)) { 71 | javaType = ByteArray; 72 | } else if (dataType.contains(BOOLEAN) || dataType.contains(BIT) || dataType.contains(DK_CM_SMALLINT)) { 73 | javaType = Boolean; 74 | } else if (dataType.contains(CHAR) || dataType.contains(TEXT) || dataType.contains(CLOB)) { 75 | javaType = String; 76 | } else if (dataType.contains(LONG) || dataType.contains(BIGINT) || dataType.contains(INT8)) { 77 | javaType = Long; 78 | } else if (dataType.contains(INT)) { 79 | javaType = Integer; 80 | } else if (dataType.contains(FLOAT)) { 81 | javaType = Float; 82 | } else if (dataType.contains(NUMBER) || dataType.contains(DECIMAL) || dataType.contains(DOUBLE)) { 83 | javaType = Double; 84 | } else if (dataType.contains(DATE) || dataType.contains(TIME)) { 85 | javaType = Date; 86 | } else if (dataType.contains(OBJECT)) { 87 | javaType = Object; 88 | } 89 | } 90 | return javaType; 91 | } 92 | 93 | /** 94 | * 根据数据库类型获取对应JdbcType 95 | * @param dataType 96 | * @return 97 | */ 98 | public static String getJdbcType(String dataType) { 99 | String jdbcType = dataType.toUpperCase(); 100 | if (dataType.contains(BINARY) || dataType.contains(CHAR_FOR_BIT_DATA) || dataType.contains(RAW) 101 | || dataType.contains(IMAGE) || dataType.contains(BYTE) || dataType.contains(DK_CM_BLOB)) { 102 | jdbcType = VARBINARY; 103 | } else if (dataType.contains(BLOB)) { 104 | jdbcType = BLOB; 105 | } else if (dataType.contains(CLOB)) { 106 | jdbcType = CLOB; 107 | } else if (dataType.contains(BOOLEAN)) { 108 | jdbcType = BOOLEAN; 109 | } else if (dataType.contains(BIT) || dataType.contains(DK_CM_SMALLINT)) { 110 | jdbcType = BIT; 111 | } else if (jdbcType.equalsIgnoreCase(TINYINT)) { 112 | jdbcType = TINYINT; 113 | } else if (jdbcType.equalsIgnoreCase(SMALLINT)) { 114 | jdbcType = SMALLINT; 115 | } else if (jdbcType.equalsIgnoreCase(INT) || jdbcType.contains(INTEGER)) { 116 | jdbcType = INTEGER; 117 | } else if (jdbcType.equalsIgnoreCase(DATE)) { 118 | jdbcType = DATE; 119 | } else if (jdbcType.equalsIgnoreCase(TIME)) { 120 | jdbcType = TIME; 121 | } else if(jdbcType.contains(DATE) || jdbcType.contains(TIME)) { 122 | jdbcType = TIMESTAMP; 123 | } else if(jdbcType.contains(FLOAT)) { 124 | jdbcType = FLOAT; 125 | } else if(jdbcType.contains(DOUBLE)) { 126 | jdbcType = DOUBLE; 127 | } else if(jdbcType.contains(DECIMAL)) { 128 | jdbcType = DECIMAL; 129 | } else if(jdbcType.contains(NUMBER) || jdbcType.contains(NUMERIC)) { 130 | jdbcType = NUMERIC; 131 | } else if(jdbcType.contains(TEXT) || jdbcType.contains(LONGVARCHAR)) { 132 | jdbcType = LONGVARCHAR; 133 | } else if(jdbcType.contains(VARCHAR)) { 134 | jdbcType = VARCHAR; 135 | } 136 | return jdbcType; 137 | } 138 | /** 139 | * 根据数据库类型判断是否需要加引号 140 | * @param dataType 141 | * @return 142 | */ 143 | public static Boolean checkType(String dataType) { 144 | String jdbcType = getJdbcType(dataType.toUpperCase()); 145 | if(jdbcType.equals(VARBINARY)||jdbcType.equals(BLOB)||jdbcType.equals(CLOB) 146 | ||jdbcType.equals(SMALLINT)||jdbcType.equals(TIMESTAMP)||jdbcType.equals(LONGVARCHAR) 147 | ||jdbcType.equals(VARCHAR)){ 148 | return true; 149 | } 150 | return false; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/utils/ReflectUtil.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.utils; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * 反射方法 9 | * @author chenhj 10 | */ 11 | public class ReflectUtil { 12 | /** 13 | * 利用反射获取指定对象的指定属性 14 | * 15 | * @param obj 目标对象 16 | * @param fieldName 目标属性 17 | * @return 目标属性的值 18 | */ 19 | public static Object getFieldValue(Object obj, String fieldName) { 20 | Object result = null; 21 | Field field = ReflectUtil.getField(obj, fieldName); 22 | if (field != null) { 23 | field.setAccessible(true); 24 | try { 25 | result = field.get(obj); 26 | } catch (IllegalArgumentException e) { 27 | // TODO Auto-generated catch block 28 | e.printStackTrace(); 29 | } catch (IllegalAccessException e) { 30 | // TODO Auto-generated catch block 31 | e.printStackTrace(); 32 | } 33 | } 34 | return result; 35 | } 36 | 37 | /** 38 | * 利用反射获取指定对象里面的指定属性 39 | * 40 | * @param obj 目标对象 41 | * @param fieldName 目标属性 42 | * @return 目标字段 43 | */ 44 | private static Field getField(Object obj, String fieldName) { 45 | Field field = null; 46 | for (Class clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { 47 | try { 48 | field = clazz.getDeclaredField(fieldName); 49 | break; 50 | } catch (NoSuchFieldException e) { 51 | // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。 52 | } 53 | } 54 | return field; 55 | } 56 | 57 | /** 58 | * 利用反射设置指定对象的指定属性为指定的值 59 | * 60 | * @param obj 目标对象 61 | * @param fieldName 目标属性 62 | * @param fieldValue 目标值 63 | */ 64 | public static void setFieldValue(Object obj, String fieldName, String fieldValue) { 65 | Field field = ReflectUtil.getField(obj, fieldName); 66 | if (field != null) { 67 | try { 68 | field.setAccessible(true); 69 | field.set(obj, fieldValue); 70 | } catch (IllegalArgumentException e) { 71 | // TODO Auto-generated catch block 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | // TODO Auto-generated catch block 75 | e.printStackTrace(); 76 | } 77 | } 78 | } 79 | 80 | 81 | /** 82 | * 根据文件路径 获取反射对象并执行对应方法 83 | * @author chenhj 84 | */ 85 | public static Object reflectByPath(String path){ 86 | try { 87 | //获取类名 88 | String className = path.substring(0, path.lastIndexOf(".")); 89 | //获取方法名 90 | String methodName = path.substring(path.lastIndexOf(".") + 1, path.length()); 91 | // 获取字节码文件对象 92 | Class c = Class.forName(className); 93 | 94 | Constructor con = c.getConstructor(); 95 | Object obj = con.newInstance(); 96 | 97 | // public Method getMethod(String name,Class... parameterTypes) 98 | // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型 99 | Method method = c.getMethod(methodName); 100 | // 调用obj对象的 method 方法 101 | return method.invoke(obj); 102 | }catch (Exception e){ 103 | e.printStackTrace(); 104 | } 105 | return null; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/utils/SqlSpliceUtils.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.utils; 2 | 3 | import com.chj.common.sys.ColumnInfo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author chenhj 9 | * @description sql拼接工具类 10 | * @create 2018-05-07 下午2:53 11 | */ 12 | public class SqlSpliceUtils { 13 | public static String spliceColumnInfos(String tableName, List columnInfoList) { 14 | StringBuilder stringBuilder = new StringBuilder("SELECT * FROM ").append(tableName).append(" WHERE "); 15 | 16 | for (ColumnInfo columnInfo:columnInfoList) { 17 | stringBuilder.append(columnInfo.getColumnName()).append("="); 18 | if(DataTypeUtils.checkType(columnInfo.getColumnType())){ 19 | stringBuilder.append("'"); 20 | } 21 | stringBuilder.append(columnInfo.getColumnValue()); 22 | if(DataTypeUtils.checkType(columnInfo.getColumnType())){ 23 | stringBuilder.append("'"); 24 | } 25 | stringBuilder.append(" and "); 26 | } 27 | stringBuilder.delete(stringBuilder.lastIndexOf("and"),stringBuilder.lastIndexOf("and")+3); 28 | return stringBuilder.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com.chj.common/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.chj.common.utils; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * 字符串工具类 8 | * 9 | * @author chenhj 10 | * @date Nov 10, 2018 11 | */ 12 | public class StringUtils { 13 | 14 | private static Pattern linePattern = Pattern.compile("_(\\w)"); 15 | private static Pattern humpPattern = Pattern.compile("[A-Z]"); 16 | 17 | /** 18 | * 判空操作 19 | * 20 | * @param value 21 | * @return 22 | */ 23 | public static boolean isEmpty(String value) { 24 | return value == null || "".equals(value) || "null".equals(value) || "undefined".equals(value); 25 | } 26 | 27 | /** 28 | * 下划线转驼峰 29 | * 30 | * @param str 31 | * @return 32 | */ 33 | public static String lineToHump(String str) { 34 | str = str.toLowerCase(); 35 | Matcher matcher = linePattern.matcher(str); 36 | StringBuffer sb = new StringBuffer(); 37 | while (matcher.find()) { 38 | matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); 39 | } 40 | matcher.appendTail(sb); 41 | return sb.toString(); 42 | } 43 | 44 | /** 45 | * 驼峰转下划线,效率比上面高 46 | * 47 | * @param str 48 | * @return 49 | */ 50 | public static String humpToLine(String str) { 51 | Matcher matcher = humpPattern.matcher(str); 52 | StringBuffer sb = new StringBuffer(); 53 | while (matcher.find()) { 54 | matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase()); 55 | } 56 | matcher.appendTail(sb); 57 | return sb.toString(); 58 | } 59 | 60 | /** 61 | * 首字母转小写 62 | * @param str 63 | * @return 64 | */ 65 | public static String uncapitalize(String str) { 66 | if (Character.isLowerCase(str.charAt(0))){ 67 | return str; 68 | }else{ 69 | return (new StringBuilder()).append(Character.toLowerCase(str.charAt(0))).append(str.substring(1)).toString(); 70 | } 71 | 72 | } 73 | 74 | /** 75 | * 首字母转大写 76 | * @param str 77 | * @return 78 | */ 79 | public static String capitalize(String str) { 80 | if (Character.isUpperCase(str.charAt(0))){ 81 | return str; 82 | }else{ 83 | return (new StringBuilder()).append(Character.toUpperCase(str.charAt(0))).append(str.substring(1)).toString(); 84 | } 85 | } 86 | } 87 | --------------------------------------------------------------------------------