diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java index 72563a4f..17b36f1b 100755 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java @@ -16,6 +16,7 @@ package com.pig4cloud.pig.auth.config; +import com.pig4cloud.pig.auth.converter.CustomAccessTokenConverter; import com.pig4cloud.pig.common.core.constant.CacheConstants; import com.pig4cloud.pig.common.core.constant.SecurityConstants; import com.pig4cloud.pig.common.security.component.PigWebResponseExceptionTranslator; @@ -63,10 +64,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap @Override @SneakyThrows public void configure(ClientDetailsServiceConfigurer clients) { - PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource); - clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); - clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); - clients.withClientDetails(clientDetailsService); + clients.withClientDetails(pigClientDetailsService()); } @Override @@ -80,7 +78,8 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap .tokenEnhancer(tokenEnhancer()).userDetailsService(userDetailsService) .authenticationManager(authenticationManager).reuseRefreshTokens(false) .pathMapping("/oauth/confirm_access", "/token/confirm_access") - .exceptionTranslator(new PigWebResponseExceptionTranslator()); + .exceptionTranslator(new PigWebResponseExceptionTranslator()) + .accessTokenConverter(new CustomAccessTokenConverter(pigClientDetailsService())); } @Bean @@ -104,4 +103,12 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap }; } + @Bean + public PigClientDetailsService pigClientDetailsService() { + PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource); + clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); + clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); + return clientDetailsService; + } + } diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/converter/CustomAccessTokenConverter.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/converter/CustomAccessTokenConverter.java new file mode 100644 index 00000000..e35dc594 --- /dev/null +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/converter/CustomAccessTokenConverter.java @@ -0,0 +1,37 @@ +package com.pig4cloud.pig.auth.converter; + +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.security.service.PigClientDetailsService; +import com.pig4cloud.pig.common.security.service.PigUser; +import lombok.RequiredArgsConstructor; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; + +import java.util.Map; + +/** + * @author hccake + */ +@RequiredArgsConstructor +public class CustomAccessTokenConverter extends DefaultAccessTokenConverter { + + final PigClientDetailsService pigClientDetailsService; + + @Override + @SuppressWarnings("unchecked") + public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + Map response = (Map) super.convertAccessToken(token, authentication); + + ClientDetails clientDetails = pigClientDetailsService + .loadClientByClientId(authentication.getOAuth2Request().getClientId()); + if (clientDetails != null && clientDetails.getScope().contains("read_data_scope")) { + PigUser principal = (PigUser) authentication.getPrincipal(); + response.put(SecurityConstants.DETAILS_USER_DATA_SCOPE, principal.getUserDataScope()); + } + + return response; + } + +} diff --git a/pig-common/pig-common-bom/pom.xml b/pig-common/pig-common-bom/pom.xml index 2e86807a..9aa82976 100644 --- a/pig-common/pig-common-bom/pom.xml +++ b/pig-common/pig-common-bom/pom.xml @@ -25,6 +25,9 @@ 1.2.75 1.5.24 3.4.3.3 + 3.5.7 + 4.1 + 5.18.3 2.0.3 1.0.0 1.0.1 @@ -38,6 +41,11 @@ pig-common-core ${pig.common.version} + + com.pig4cloud + pig-common-datascope + ${pig.common.version} + com.pig4cloud pig-common-datasource @@ -123,12 +131,22 @@ oss-spring-boot-starter ${oss.version} - + com.baomidou mybatis-plus-boot-starter ${mybatis-plus.version} + + org.mybatis + mybatis + ${mybatis.version} + + + com.github.jsqlparser + jsqlparser + ${jsqlparser.version} + org.springframework.boot diff --git a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/SecurityConstants.java b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/SecurityConstants.java index c51aab5b..8cf51a60 100755 --- a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/SecurityConstants.java +++ b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/SecurityConstants.java @@ -109,6 +109,11 @@ public interface SecurityConstants { */ String DETAILS_LICENSE = "license"; + /** + * 用户数据权限信息 + */ + String DETAILS_USER_DATA_SCOPE = "user_data_scope"; + /** * 验证码有效期,默认 60秒 */ diff --git a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/enums/DataScopeTypeEnum.java b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/enums/DataScopeTypeEnum.java new file mode 100644 index 00000000..0ed6a969 --- /dev/null +++ b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/enums/DataScopeTypeEnum.java @@ -0,0 +1,49 @@ +package com.pig4cloud.pig.common.core.constant.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据权限范围类型 + * @author hccake + */ +@Getter +@AllArgsConstructor +public enum DataScopeTypeEnum { + + /** + * 查询全部数据 + */ + ALL(0), + + /** + * 本人 + */ + SELF(1), + + /** + * 本人及子级 + */ + SELF_CHILD_LEVEL(2), + + /** + * 本级 + */ + LEVEL(3), + + /** + * 本级及子级 + */ + LEVEL_CHILD_LEVEL(4), + + /** + * 自定义 + */ + CUSTOM(5); + + /** + * 类型 + */ + private final Integer type; + +} diff --git a/pig-common/pig-common-datascope/pom.xml b/pig-common/pig-common-datascope/pom.xml new file mode 100644 index 00000000..df6236b7 --- /dev/null +++ b/pig-common/pig-common-datascope/pom.xml @@ -0,0 +1,38 @@ + + + + pig-common + com.pig4cloud + 3.3.4 + + 4.0.0 + + pig-common-datascope + + + + + org.slf4j + slf4j-api + + + com.github.jsqlparser + jsqlparser + + + org.mybatis + mybatis + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot-autoconfigure + + + + \ No newline at end of file diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScope.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScope.java new file mode 100644 index 00000000..b137fd26 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScope.java @@ -0,0 +1,36 @@ +package com.pigcloud.pig.common.datascope; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; + +import java.util.Collection; + +/** + * @author Hccake 2020/9/28 + * @version 1.0 + */ +public interface DataScope { + + /** + * 数据所对应的资源 + * @return 资源标识 + */ + String getResource(); + + /** + * 该资源相关的所有表,推荐使用 Set 类型。
+ * 如需忽略表名大小写判断,则可以使用 TreeSet,并设置忽略大小写的自定义Comparator。
+ * eg. new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + * @return tableNames + */ + Collection getTableNames(); + + /** + * 根据表名和表别名,动态生成的 where/or 筛选条件 + * @param tableName 表名 + * @param tableAlias 表别名,可能为空 + * @return 数据规则表达式 + */ + Expression getExpression(String tableName, Alias tableAlias); + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScopeAutoConfiguration.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScopeAutoConfiguration.java new file mode 100644 index 00000000..6928850a --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/DataScopeAutoConfiguration.java @@ -0,0 +1,54 @@ +package com.pigcloud.pig.common.datascope; + +import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler; +import com.pigcloud.pig.common.datascope.handler.DefaultDataPermissionHandler; +import com.pigcloud.pig.common.datascope.interceptor.DataPermissionAnnotationAdvisor; +import com.pigcloud.pig.common.datascope.interceptor.DataPermissionInterceptor; +import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +import java.util.List; + +/** + * @author hccake + */ +@RequiredArgsConstructor +@ConditionalOnBean(DataScope.class) +public class DataScopeAutoConfiguration { + + /** + * 数据权限处理器 + * @param dataScopeList 需要控制的数据范围集合 + * @return DataPermissionHandler + */ + @Bean + @ConditionalOnMissingBean + public DataPermissionHandler dataPermissionHandler(List dataScopeList) { + return new DefaultDataPermissionHandler(dataScopeList); + } + + /** + * 数据权限注解 Advisor,用于处理数据权限的链式调用关系 + * @return DataPermissionAnnotationAdvisor + */ + @Bean + @ConditionalOnMissingBean(DataPermissionAnnotationAdvisor.class) + public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() { + return new DataPermissionAnnotationAdvisor(); + } + + /** + * mybatis 拦截器,用于拦截处理 sql + * @param dataPermissionHandler 数据权限处理器 + * @return DataPermissionInterceptor + */ + @Bean + @ConditionalOnMissingBean + public DataPermissionInterceptor dataPermissionInterceptor(DataPermissionHandler dataPermissionHandler) { + return new DataPermissionInterceptor(new DataScopeSqlProcessor(), dataPermissionHandler); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/annotation/DataPermission.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/annotation/DataPermission.java new file mode 100644 index 00000000..0c6ef980 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/annotation/DataPermission.java @@ -0,0 +1,35 @@ +package com.pigcloud.pig.common.datascope.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限注解,注解在 Mapper类 或者 对应方法上 用于提供该 mapper 对应表,所需控制的实体信息 + * @author Hccake 2020/9/27 + * @version 1.0 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + /** + * 当前类或方法是否忽略数据权限 + * @return boolean 默认返回 false + */ + boolean ignore() default false; + + /** + * 仅对指定资源类型进行数据权限控制,只在开启情况下有效,当该数组有值时,exclude不生效 + * @see DataPermission#excludeResources + * @return 资源类型数组 + */ + String[] includeResources() default {}; + + /** + * 对指定资源类型跳过数据权限控制,只在开启情况下有效,当该includeResources有值时,exclude不生效 + * @see DataPermission#includeResources + * @return 资源类型数组 + */ + String[] excludeResources() default {}; + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DataPermissionHandler.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DataPermissionHandler.java new file mode 100644 index 00000000..a5620000 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DataPermissionHandler.java @@ -0,0 +1,35 @@ +package com.pigcloud.pig.common.datascope.handler; + +import com.pigcloud.pig.common.datascope.DataScope; + +import java.util.List; + +/** + * 数据权限处理器 + * + * @author Hccake 2020/9/28 + * @version 1.0 + */ +public interface DataPermissionHandler { + + /** + * 系统配置的所有的数据范围 + * @return 数据范围集合 + */ + List dataScopes(); + + /** + * 根据权限注解过滤后的数据范围集合 + * @param mappedStatementId Mapper方法ID + * @return 数据范围集合 + */ + List filterDataScopes(String mappedStatementId); + + /** + * 是否忽略权限控制,用于及早的忽略控制,例如管理员直接放行,而不必等到DataScope中再进行过滤处理,提升效率 + * @return boolean true: 忽略,false: 进行权限控制 + * @param mappedStatementId Mapper方法ID + */ + boolean ignorePermissionControl(String mappedStatementId); + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DefaultDataPermissionHandler.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DefaultDataPermissionHandler.java new file mode 100644 index 00000000..4622936e --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/handler/DefaultDataPermissionHandler.java @@ -0,0 +1,76 @@ +package com.pigcloud.pig.common.datascope.handler; + +import com.pigcloud.pig.common.datascope.DataScope; +import com.pigcloud.pig.common.datascope.annotation.DataPermission; +import com.pigcloud.pig.common.datascope.holder.DataPermissionAnnotationHolder; +import lombok.RequiredArgsConstructor; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 默认的数据权限控制处理器 + * + * @author Hccake 2021/1/27 + * @version 1.0 + */ +@RequiredArgsConstructor +public class DefaultDataPermissionHandler implements DataPermissionHandler { + + private final List dataScopes; + + /** + * 系统配置的所有的数据范围 + * @return 数据范围集合 + */ + @Override + public List dataScopes() { + return dataScopes; + } + + /** + * 系统配置的所有的数据范围 + * @param mappedStatementId Mapper方法ID + * @return 数据范围集合 + */ + @Override + public List filterDataScopes(String mappedStatementId) { + if (this.dataScopes == null || this.dataScopes.isEmpty()) { + return new ArrayList<>(); + } + // 获取当前方法对应的权限注解,根据注解进行数据范围控制的过滤 + DataPermission dataPermission = DataPermissionAnnotationHolder.peek(); + if (dataPermission == null) { + return dataScopes; + } + + if (dataPermission.ignore()) { + return new ArrayList<>(); + } + + // 当指定了只包含的资源时,只对该资源的DataScope + if (dataPermission.includeResources().length > 0) { + Set a = new HashSet<>(Arrays.asList(dataPermission.includeResources())); + return dataScopes.stream().filter(x -> a.contains(x.getResource())).collect(Collectors.toList()); + } + + // 当未指定只包含的资源,且指定了排除的资源时,则排除此部分资源的 DataScope + if (dataPermission.excludeResources().length > 0) { + Set a = new HashSet<>(Arrays.asList(dataPermission.excludeResources())); + return dataScopes.stream().filter(x -> !a.contains(x.getResource())).collect(Collectors.toList()); + } + + return dataScopes; + } + + /** + * 是否忽略权限控制,默认不忽略 + * @param mappedStatementId Mapper方法ID + * @return always false + */ + @Override + public boolean ignorePermissionControl(String mappedStatementId) { + return false; + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataPermissionAnnotationHolder.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataPermissionAnnotationHolder.java new file mode 100644 index 00000000..7ceb63b5 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataPermissionAnnotationHolder.java @@ -0,0 +1,58 @@ +package com.pigcloud.pig.common.datascope.holder; + +import com.pigcloud.pig.common.datascope.annotation.DataPermission; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * 数据权限注解的持有者,使用栈存储调用链中各方法对应数据权限注解 + * + * @author hccake + */ +public final class DataPermissionAnnotationHolder { + + private DataPermissionAnnotationHolder() { + } + + /** + * 使用栈存储 DataPermission,便于在方法嵌套调用时使用不同的数据权限控制。 + */ + private static final ThreadLocal> DATA_PERMISSIONS = ThreadLocal.withInitial(ArrayDeque::new); + + /** + * 获取当前的 DataPermission 注解 + * @return DataPermission + */ + public static DataPermission peek() { + return DATA_PERMISSIONS.get().peek(); + } + + /** + * 入栈一个 DataPermission 注解 + * @return DataPermission + */ + public static DataPermission push(DataPermission dataPermission) { + DATA_PERMISSIONS.get().push(dataPermission); + return dataPermission; + } + + /** + * 弹出最顶部 DataPermission + */ + public static void poll() { + Deque deque = DATA_PERMISSIONS.get(); + // 当没有元素时,清空 ThreadLocal + if (deque.poll() == null) { + DATA_PERMISSIONS.remove(); + } + } + + /** + * 清除 TreadLocal + */ + public static void clear() { + DATA_PERMISSIONS.remove(); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataScopeHolder.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataScopeHolder.java new file mode 100644 index 00000000..e47dad32 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/holder/DataScopeHolder.java @@ -0,0 +1,41 @@ +package com.pigcloud.pig.common.datascope.holder; + +import com.pigcloud.pig.common.datascope.DataScope; + +import java.util.List; + +/** + * DataScope 持有者。 方便解析 SQL 时的参数透传 + * + * @author hccake + */ +public final class DataScopeHolder { + + private DataScopeHolder() { + } + + private static final ThreadLocal> DATA_SCOPES = new ThreadLocal<>(); + + /** + * get dataScope + * @return dataScopes + */ + public static List get() { + return DATA_SCOPES.get(); + } + + /** + * 添加 dataScope + */ + public static void set(List dataScopes) { + DATA_SCOPES.set(dataScopes); + } + + /** + * 删除 dataScope + */ + public static void remove() { + DATA_SCOPES.remove(); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationAdvisor.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationAdvisor.java new file mode 100644 index 00000000..f333d697 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationAdvisor.java @@ -0,0 +1,34 @@ +package com.pigcloud.pig.common.datascope.interceptor; + +import com.pigcloud.pig.common.datascope.annotation.DataPermission; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.aopalliance.aop.Advice; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; +import org.springframework.aop.support.ComposablePointcut; +import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; + +/** + * @author hccake + */ +@Getter +@EqualsAndHashCode(callSuper = true) +public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor { + + private final Advice advice; + + private final Pointcut pointcut; + + public DataPermissionAnnotationAdvisor() { + this.advice = new DataPermissionAnnotationInterceptor(); + this.pointcut = buildPointcut(); + } + + protected Pointcut buildPointcut() { + Pointcut cpc = new AnnotationMatchingPointcut(DataPermission.class, true); + Pointcut mpc = new AnnotationMatchingPointcut(null, DataPermission.class, true); + return new ComposablePointcut(cpc).union(mpc); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationInterceptor.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationInterceptor.java new file mode 100644 index 00000000..b9ae8fdf --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionAnnotationInterceptor.java @@ -0,0 +1,35 @@ +package com.pigcloud.pig.common.datascope.interceptor; + +import com.pigcloud.pig.common.datascope.annotation.DataPermission; +import com.pigcloud.pig.common.datascope.holder.DataPermissionAnnotationHolder; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import java.lang.reflect.Method; + +/** + * DataPermission注解的拦截器,在执行方法前将当前方法的对应注解压栈,执行后弹出注解 + * + * @author hccake + */ +public class DataPermissionAnnotationInterceptor implements MethodInterceptor { + + @Override + public Object invoke(MethodInvocation methodInvocation) throws Throwable { + // 当前方法 + Method method = methodInvocation.getMethod(); + // 获取执行类 + Object invocationThis = methodInvocation.getThis(); + Class clazz = invocationThis != null ? invocationThis.getClass() : method.getDeclaringClass(); + // 寻找对应的 DataPermission 注解属性 + DataPermission dataPermission = DataPermissionFinder.findDataPermission(method, clazz); + DataPermissionAnnotationHolder.push(dataPermission); + try { + return methodInvocation.proceed(); + } + finally { + DataPermissionAnnotationHolder.poll(); + } + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionFinder.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionFinder.java new file mode 100644 index 00000000..1d4d1e30 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionFinder.java @@ -0,0 +1,65 @@ +package com.pigcloud.pig.common.datascope.interceptor; + +import com.pigcloud.pig.common.datascope.annotation.DataPermission; +import org.springframework.core.MethodClassKey; +import org.springframework.core.annotation.AnnotatedElementUtils; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * {@link DataPermission} 注解的查找者。用于查询当前方法对应的 DataPermission 注解环境,当方法上没有找到时,会去类上寻找。 + * + * @author hccake + */ +@DataPermission +public final class DataPermissionFinder { + + private DataPermissionFinder() { + + } + + private static final Map DATA_PERMISSION_CACHE = new ConcurrentHashMap<>(1024); + + /** + * 提供一个默认的空值注解,用于缓存空值占位使用 + */ + private static final DataPermission EMPTY_DATA_PERMISSION = DataPermissionFinder.class + .getAnnotation(DataPermission.class); + + /** + * 缓存的 key 值 + * @param method 方法 + * @param clazz 类 + * @return key + */ + private static Object getCacheKey(Method method, Class clazz) { + return new MethodClassKey(method, clazz); + } + + /** + * 从缓存中获取数据权限注解 优先获取方法上的注解,再获取类上的注解 + * @param method 当前方法 + * @param clazz 当前类 + * @return 当前方法有效的数据权限注解 + */ + public static DataPermission findDataPermission(Method method, Class clazz) { + Object methodKey = getCacheKey(method, clazz); + + if (DATA_PERMISSION_CACHE.containsKey(methodKey)) { + DataPermission dataPermission = DATA_PERMISSION_CACHE.get(methodKey); + // 判断是否和缓存的空注解是同一个对象 + return EMPTY_DATA_PERMISSION == dataPermission ? null : dataPermission; + } + + // 先查方法,如果方法上没有,则使用类上 + DataPermission dataPermission = AnnotatedElementUtils.findMergedAnnotation(method, DataPermission.class); + if (dataPermission == null) { + dataPermission = AnnotatedElementUtils.findMergedAnnotation(clazz, DataPermission.class); + } + DATA_PERMISSION_CACHE.put(methodKey, dataPermission == null ? EMPTY_DATA_PERMISSION : dataPermission); + return dataPermission; + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionInterceptor.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionInterceptor.java new file mode 100644 index 00000000..9caba7ba --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/interceptor/DataPermissionInterceptor.java @@ -0,0 +1,72 @@ +package com.pigcloud.pig.common.datascope.interceptor; + +import com.pigcloud.pig.common.datascope.DataScope; +import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler; +import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor; +import com.pigcloud.pig.common.datascope.util.PluginUtils; +import lombok.RequiredArgsConstructor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; + +import java.sql.Connection; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author Hccake 2020/9/28 + * @version 1.0 + */ +@RequiredArgsConstructor +@Intercepts({ + @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) }) +public class DataPermissionInterceptor implements Interceptor { + + private final DataScopeSqlProcessor dataScopeSqlProcessor; + + private final DataPermissionHandler dataPermissionHandler; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + // 第一版,测试用 + Object target = invocation.getTarget(); + StatementHandler sh = (StatementHandler) target; + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + SqlCommandType sct = ms.getSqlCommandType(); + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + String mappedStatementId = ms.getId(); + + // 根据用户权限判断是否需要拦截,例如管理员可以查看所有,则直接放行 + if (dataPermissionHandler.ignorePermissionControl(mappedStatementId)) { + return invocation.proceed(); + } + + List dataScopes = dataPermissionHandler.filterDataScopes(mappedStatementId); + if (dataScopes == null || dataScopes.isEmpty()) { + return invocation.proceed(); + } + + // 根据 DataScopes 进行数据权限的 sql 处理 + if (sct == SqlCommandType.SELECT) { + mpBs.sql(dataScopeSqlProcessor.parserSingle(mpBs.sql(), dataScopes)); + } + else if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + mpBs.sql(dataScopeSqlProcessor.parserMulti(mpBs.sql(), dataScopes)); + } + + // 执行 sql + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + if (target instanceof StatementHandler) { + return Plugin.wrap(target, this); + } + return target; + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/parser/JsqlParserSupport.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/parser/JsqlParserSupport.java new file mode 100644 index 00000000..ab12c2ed --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/parser/JsqlParserSupport.java @@ -0,0 +1,114 @@ +package com.pigcloud.pig.common.datascope.parser; + +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.Update; + +/** + * https://github.com/JSQLParser/JSqlParser + * + * @author miemie hccake + * @since 2020-06-22 + */ +@Slf4j +public abstract class JsqlParserSupport { + + public String parserSingle(String sql, Object obj) { + if (log.isDebugEnabled()) { + log.debug("original SQL: " + sql); + } + try { + Statement statement = CCJSqlParserUtil.parse(sql); + return processParser(statement, 0, sql, obj); + } + catch (JSQLParserException e) { + throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e); + } + } + + public String parserMulti(String sql, Object obj) { + if (log.isDebugEnabled()) { + log.debug("original SQL: " + sql); + } + try { + // fixed github pull/295 + StringBuilder sb = new StringBuilder(); + Statements statements = CCJSqlParserUtil.parseStatements(sql); + int i = 0; + for (Statement statement : statements.getStatements()) { + if (i > 0) { + sb.append(";"); + } + sb.append(processParser(statement, i, sql, obj)); + i++; + } + return sb.toString(); + } + catch (JSQLParserException e) { + throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e); + } + } + + /** + * 执行 SQL 解析 + * @param statement JsqlParser Statement + * @return sql + */ + protected String processParser(Statement statement, int index, String sql, Object obj) { + if (log.isDebugEnabled()) { + log.debug("SQL to parse, SQL: " + sql); + } + if (statement instanceof Insert) { + this.processInsert((Insert) statement, index, sql, obj); + } + else if (statement instanceof Select) { + this.processSelect((Select) statement, index, sql, obj); + } + else if (statement instanceof Update) { + this.processUpdate((Update) statement, index, sql, obj); + } + else if (statement instanceof Delete) { + this.processDelete((Delete) statement, index, sql, obj); + } + sql = statement.toString(); + if (log.isDebugEnabled()) { + log.debug("parse the finished SQL: " + sql); + } + return sql; + } + + /** + * 新增 + */ + protected void processInsert(Insert insert, int index, String sql, Object obj) { + throw new UnsupportedOperationException(); + } + + /** + * 删除 + */ + protected void processDelete(Delete delete, int index, String sql, Object obj) { + throw new UnsupportedOperationException(); + } + + /** + * 更新 + */ + protected void processUpdate(Update update, int index, String sql, Object obj) { + throw new UnsupportedOperationException(); + } + + /** + * 查询 + */ + protected void processSelect(Select select, int index, String sql, Object obj) { + throw new UnsupportedOperationException(); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/processor/DataScopeSqlProcessor.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/processor/DataScopeSqlProcessor.java new file mode 100644 index 00000000..7248be92 --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/processor/DataScopeSqlProcessor.java @@ -0,0 +1,321 @@ +package com.pigcloud.pig.common.datascope.processor; + +import com.pigcloud.pig.common.datascope.DataScope; +import com.pigcloud.pig.common.datascope.holder.DataScopeHolder; +import com.pigcloud.pig.common.datascope.parser.JsqlParserSupport; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.expression.operators.relational.ItemsList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.*; +import net.sf.jsqlparser.statement.update.Update; + +import java.util.List; +import java.util.Objects; + +/** + * 数据权限 sql 处理器 参考 mybatis-plus 租户拦截器,解析 sql where 部分,进行查询表达式注入 + * + * @author Hccake 2020/9/26 + * @version 1.0 + */ +@RequiredArgsConstructor +@Slf4j +public class DataScopeSqlProcessor extends JsqlParserSupport { + + /** + * select 类型SQL处理 + * @param select jsqlparser Statement Select + */ + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + List dataScopes = (List) obj; + try { + // dataScopes 放入 ThreadLocal 方便透传 + DataScopeHolder.set(dataScopes); + processSelectBody(select.getSelectBody()); + List withItemsList = select.getWithItemsList(); + if (withItemsList != null && !withItemsList.isEmpty()) { + withItemsList.forEach(this::processSelectBody); + } + } + finally { + // 必须清空 ThreadLocal + DataScopeHolder.remove(); + } + } + + protected void processSelectBody(SelectBody selectBody) { + if (selectBody == null) { + return; + } + if (selectBody instanceof PlainSelect) { + processPlainSelect((PlainSelect) selectBody); + } + else if (selectBody instanceof WithItem) { + WithItem withItem = (WithItem) selectBody; + processSelectBody(withItem.getSubSelect().getSelectBody()); + } + else { + SetOperationList operationList = (SetOperationList) selectBody; + List selectBodys = operationList.getSelects(); + if (selectBodys != null && !selectBodys.isEmpty()) { + selectBodys.forEach(this::processSelectBody); + } + } + } + + /** + * insert 类型SQL处理 + * @param insert jsqlparser Statement Insert + */ + @Override + protected void processInsert(Insert insert, int index, String sql, Object obj) { + // insert 暂时不处理 + } + + /** + * update 类型SQL处理 + * @param update jsqlparser Statement Update + */ + @Override + protected void processUpdate(Update update, int index, String sql, Object obj) { + List dataScopes = (List) obj; + try { + // dataScopes 放入 ThreadLocal 方便透传 + DataScopeHolder.set(dataScopes); + update.setWhere(this.injectExpression(update.getWhere(), update.getTable())); + } + finally { + // 必须清空 ThreadLocal + DataScopeHolder.remove(); + } + } + + /** + * delete 类型SQL处理 + * @param delete jsqlparser Statement Delete + */ + @Override + protected void processDelete(Delete delete, int index, String sql, Object obj) { + List dataScopes = (List) obj; + try { + // dataScopes 放入 ThreadLocal 方便透传 + DataScopeHolder.set(dataScopes); + delete.setWhere(this.injectExpression(delete.getWhere(), delete.getTable())); + } + finally { + // 必须清空 ThreadLocal + DataScopeHolder.remove(); + } + } + + /** + * 处理 PlainSelect + */ + protected void processPlainSelect(PlainSelect plainSelect) { + FromItem fromItem = plainSelect.getFromItem(); + Expression where = plainSelect.getWhere(); + processWhereSubSelect(where); + if (fromItem instanceof Table) { + Table fromTable = (Table) fromItem; + // #1186 github + plainSelect.setWhere(injectExpression(where, fromTable)); + } + else { + processFromItem(fromItem); + } + // #3087 github + List selectItems = plainSelect.getSelectItems(); + if (selectItems != null && !selectItems.isEmpty()) { + selectItems.forEach(this::processSelectItem); + } + List joins = plainSelect.getJoins(); + if (joins != null && !joins.isEmpty()) { + joins.forEach(j -> { + processJoin(j); + processFromItem(j.getRightItem()); + }); + } + } + + /** + * 处理where条件内的子查询 + *

+ * 支持如下: 1. in 2. = 3. > 4. < 5. >= 6. <= 7. <> 8. EXISTS 9. NOT EXISTS + *

+ * 前提条件: 1. 子查询必须放在小括号中 2. 子查询一般放在比较操作符的右边 + * @param where where 条件 + */ + protected void processWhereSubSelect(Expression where) { + if (where == null) { + return; + } + if (where instanceof FromItem) { + processFromItem((FromItem) where); + return; + } + if (where.toString().indexOf("SELECT") > 0) { + // 有子查询 + if (where instanceof BinaryExpression) { + // 比较符号 , and , or , 等等 + BinaryExpression expression = (BinaryExpression) where; + processWhereSubSelect(expression.getLeftExpression()); + processWhereSubSelect(expression.getRightExpression()); + } + else if (where instanceof InExpression) { + // in + InExpression expression = (InExpression) where; + ItemsList itemsList = expression.getRightItemsList(); + if (itemsList instanceof SubSelect) { + processSelectBody(((SubSelect) itemsList).getSelectBody()); + } + } + else if (where instanceof ExistsExpression) { + // exists + ExistsExpression expression = (ExistsExpression) where; + processWhereSubSelect(expression.getRightExpression()); + } + else if (where instanceof NotExpression) { + // not exists + NotExpression expression = (NotExpression) where; + processWhereSubSelect(expression.getExpression()); + } + else if (where instanceof Parenthesis) { + Parenthesis expression = (Parenthesis) where; + processWhereSubSelect(expression.getExpression()); + } + } + } + + protected void processSelectItem(SelectItem selectItem) { + if (selectItem instanceof SelectExpressionItem) { + SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; + if (selectExpressionItem.getExpression() instanceof SubSelect) { + processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody()); + } + else if (selectExpressionItem.getExpression() instanceof Function) { + processFunction((Function) selectExpressionItem.getExpression()); + } + } + } + + /** + * 处理函数 + *

+ * 支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..) + *

+ *

+ * fixed gitee pulls/141 + *

+ * @param function + */ + protected void processFunction(Function function) { + ExpressionList parameters = function.getParameters(); + if (parameters != null) { + parameters.getExpressions().forEach(expression -> { + if (expression instanceof SubSelect) { + processSelectBody(((SubSelect) expression).getSelectBody()); + } + else if (expression instanceof Function) { + processFunction((Function) expression); + } + }); + } + } + + /** + * 处理子查询等 + */ + protected void processFromItem(FromItem fromItem) { + if (fromItem instanceof SubJoin) { + SubJoin subJoin = (SubJoin) fromItem; + if (subJoin.getJoinList() != null) { + subJoin.getJoinList().forEach(this::processJoin); + } + if (subJoin.getLeft() != null) { + processFromItem(subJoin.getLeft()); + } + } + else if (fromItem instanceof SubSelect) { + SubSelect subSelect = (SubSelect) fromItem; + if (subSelect.getSelectBody() != null) { + processSelectBody(subSelect.getSelectBody()); + } + } + else if (fromItem instanceof ValuesList) { + log.debug("Perform a subquery, if you do not give us feedback"); + } + else if (fromItem instanceof LateralSubSelect) { + LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem; + if (lateralSubSelect.getSubSelect() != null) { + SubSelect subSelect = lateralSubSelect.getSubSelect(); + if (subSelect.getSelectBody() != null) { + processSelectBody(subSelect.getSelectBody()); + } + } + } + } + + /** + * 处理联接语句 + */ + protected void processJoin(Join join) { + if (join.getRightItem() instanceof Table) { + Table fromTable = (Table) join.getRightItem(); + join.setOnExpression(injectExpression(join.getOnExpression(), fromTable)); + } + } + + /** + * 根据 DataScope ,将数据过滤的表达式注入原本的 where/or 条件 + * @param currentExpression Expression where/or + * @param table 表信息 + * @return 修改后的 where/or 条件 + */ + private Expression injectExpression(Expression currentExpression, Table table) { + String tableName = table.getName(); + List dataScopes = DataScopeHolder.get(); + Expression dataFilterExpression = dataScopes.stream().filter(x -> x.getTableNames().contains(tableName)) + .map(x -> x.getExpression(tableName, table.getAlias())).filter(Objects::nonNull) + .reduce(AndExpression::new).orElse(null); + + if (currentExpression == null) { + return dataFilterExpression; + } + if (dataFilterExpression == null) { + return currentExpression; + } + if (currentExpression instanceof OrExpression) { + return new AndExpression(new Parenthesis(currentExpression), dataFilterExpression); + } + else { + return new AndExpression(currentExpression, dataFilterExpression); + } + } + + /** + * 根据当前表是否有别名,动态对字段名前添加表别名 eg. 表名: table_1 as t 原始字段:column1 返回: t.column1 + * @param table 表信息 + * @param column 字段名 + * @return 原始字段名,或者添加了表别名的字段名 + */ + protected Column getAliasColumn(Table table, String column) { + StringBuilder columnBuilder = new StringBuilder(); + if (table.getAlias() != null) { + columnBuilder.append(table.getAlias().getName()).append("."); + } + columnBuilder.append(column); + return new Column(columnBuilder.toString()); + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/AnnotationUtil.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/AnnotationUtil.java new file mode 100644 index 00000000..ccead3ff --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/AnnotationUtil.java @@ -0,0 +1,64 @@ +package com.pigcloud.pig.common.datascope.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * @author Hccake 2021/1/27 + * @version 1.0 + */ +public final class AnnotationUtil { + + private AnnotationUtil() { + } + + /** + * 获取数据权限注解 优先获取方法上的注解,再获取类上的注解 + * @param mappedStatementId 类名.方法名 + * @return 数据权限注解 + */ + public static A findAnnotationByMappedStatementId(String mappedStatementId, + Class aClass) { + if (mappedStatementId == null || "".equals(mappedStatementId)) { + return null; + } + // 1.得到类路径和方法路径 + int lastIndexOfDot = mappedStatementId.lastIndexOf("."); + if (lastIndexOfDot < 0) { + return null; + } + String className = mappedStatementId.substring(0, lastIndexOfDot); + String methodName = mappedStatementId.substring(lastIndexOfDot + 1); + if ("".equals(className) || "".equals(methodName)) { + return null; + } + + // 2.字节码 + Class clazz = null; + try { + clazz = Class.forName(className); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + if (clazz == null) { + return null; + } + + A annotation = null; + // 3.得到方法上的注解 + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + String name = method.getName(); + if (methodName.equals(name)) { + annotation = method.getAnnotation(aClass); + break; + } + } + if (annotation == null) { + annotation = clazz.getAnnotation(aClass); + } + return annotation; + } + +} diff --git a/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/PluginUtils.java b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/PluginUtils.java new file mode 100644 index 00000000..f4eca99f --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/java/com/pigcloud/pig/common/datascope/util/PluginUtils.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011-2020, baomidou (jobob@qq.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.pigcloud.pig.common.datascope.util; + +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ParameterMapping; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.apache.ibatis.session.Configuration; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * 插件工具类 + * + * @author TaoYu , hubin + * @since 2017-06-20 + */ +public abstract class PluginUtils { + + public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql"; + + /** + * 获得真正的处理对象,可能多层代理. + */ + @SuppressWarnings("unchecked") + public static T realTarget(Object target) { + if (Proxy.isProxyClass(target.getClass())) { + MetaObject metaObject = SystemMetaObject.forObject(target); + return realTarget(metaObject.getValue("h.target")); + } + return (T) target; + } + + /** + * 给 BoundSql 设置 additionalParameters + * @param boundSql BoundSql + * @param additionalParameters additionalParameters + */ + public static void setAdditionalParameter(BoundSql boundSql, Map additionalParameters) { + additionalParameters.forEach(boundSql::setAdditionalParameter); + } + + public static MPBoundSql mpBoundSql(BoundSql boundSql) { + return new MPBoundSql(boundSql); + } + + public static MPStatementHandler mpStatementHandler(StatementHandler statementHandler) { + statementHandler = realTarget(statementHandler); + MetaObject object = SystemMetaObject.forObject(statementHandler); + return new MPStatementHandler(SystemMetaObject.forObject(object.getValue("delegate"))); + } + + /** + * {@link org.apache.ibatis.executor.statement.BaseStatementHandler} + */ + public static class MPStatementHandler { + + private final MetaObject statementHandler; + + MPStatementHandler(MetaObject statementHandler) { + this.statementHandler = statementHandler; + } + + public ParameterHandler parameterHandler() { + return get("parameterHandler"); + } + + public MappedStatement mappedStatement() { + return get("mappedStatement"); + } + + public Executor executor() { + return get("executor"); + } + + public MPBoundSql mPBoundSql() { + return new MPBoundSql(boundSql()); + } + + public BoundSql boundSql() { + return get("boundSql"); + } + + public Configuration configuration() { + return get("configuration"); + } + + @SuppressWarnings("unchecked") + private T get(String property) { + return (T) statementHandler.getValue(property); + } + + } + + /** + * {@link BoundSql} + */ + public static class MPBoundSql { + + private final MetaObject boundSql; + + private final BoundSql delegate; + + MPBoundSql(BoundSql boundSql) { + this.delegate = boundSql; + this.boundSql = SystemMetaObject.forObject(boundSql); + } + + public String sql() { + return delegate.getSql(); + } + + public void sql(String sql) { + boundSql.setValue("sql", sql); + } + + public List parameterMappings() { + List parameterMappings = delegate.getParameterMappings(); + return new ArrayList<>(parameterMappings); + } + + public void parameterMappings(List parameterMappings) { + boundSql.setValue("parameterMappings", Collections.unmodifiableList(parameterMappings)); + } + + public Object parameterObject() { + return get("parameterObject"); + } + + public Map additionalParameters() { + return get("additionalParameters"); + } + + @SuppressWarnings("unchecked") + private T get(String property) { + return (T) boundSql.getValue(property); + } + + } + +} diff --git a/pig-common/pig-common-datascope/src/main/resources/META-INF/spring.factories b/pig-common/pig-common-datascope/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..7437931e --- /dev/null +++ b/pig-common/pig-common-datascope/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.pigcloud.pig.common.datascope.DataScopeAutoConfiguration + diff --git a/pig-common/pig-common-datascope/src/test/java/com/pigcloud/pig/common/datascope/test/SqlParseTest.java b/pig-common/pig-common-datascope/src/test/java/com/pigcloud/pig/common/datascope/test/SqlParseTest.java new file mode 100644 index 00000000..9fdf2dc5 --- /dev/null +++ b/pig-common/pig-common-datascope/src/test/java/com/pigcloud/pig/common/datascope/test/SqlParseTest.java @@ -0,0 +1,75 @@ +package com.pigcloud.pig.common.datascope.test; + +import com.pigcloud.pig.common.datascope.DataScope; +import com.pigcloud.pig.common.datascope.handler.DefaultDataPermissionHandler; +import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler; +import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.schema.Column; +import org.junit.jupiter.api.Test; +import org.springframework.util.Assert; + +import java.util.*; + +/** + * @author Hccake 2020/9/28 + * @version 1.0 + */ +class SqlParseTest { + + @Test + void test() { + DataScope dataScope = new DataScope() { + final String columnId = "order_id"; + + @Override + public String getResource() { + return "order"; + } + + @Override + public Collection getTableNames() { + Set tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + tableNames.addAll(Arrays.asList("t_order", "t_order_info")); + return tableNames; + } + + @Override + public Expression getExpression(String tableName, Alias tableAlias) { + Column column = new Column(tableAlias == null ? columnId : tableAlias.getName() + "." + columnId); + ExpressionList expressionList = new ExpressionList(); + expressionList.setExpressions(Arrays.asList(new StringValue("1"), new StringValue("2"))); + return new InExpression(column, expressionList); + } + }; + + List dataScopes = new ArrayList<>(); + dataScopes.add(dataScope); + + DataPermissionHandler dataPermissionHandler = new DefaultDataPermissionHandler(dataScopes) { + @Override + public boolean ignorePermissionControl(String mappedStatementId) { + return false; + } + }; + + DataScopeSqlProcessor dataScopeSqlProcessor = new DataScopeSqlProcessor(); + + // DataScopeHolder.putDataScope("order", dataScope); + + String sql = "select o.order_id,o.order_name,oi.order_price " + + "from t_ORDER o left join t_order_info oi on o.order_id = oi.order_id " + + "where oi.order_price > 100"; + + String parseSql = dataScopeSqlProcessor.parserSingle(sql, dataPermissionHandler.dataScopes()); + System.out.println(parseSql); + + String trueSql = "SELECT o.order_id, o.order_name, oi.order_price FROM t_ORDER o LEFT JOIN t_order_info oi ON o.order_id = oi.order_id AND oi.order_id IN ('1', '2') WHERE oi.order_price > 100 AND o.order_id IN ('1', '2')"; + Assert.isTrue(trueSql.equals(parseSql), "sql 数据权限解析异常"); + } + +} diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/component/PigUserAuthenticationConverter.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/component/PigUserAuthenticationConverter.java index 725c25f1..e880d48d 100644 --- a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/component/PigUserAuthenticationConverter.java +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/component/PigUserAuthenticationConverter.java @@ -17,6 +17,7 @@ package com.pig4cloud.pig.common.security.component; import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.security.datascope.UserDataScope; import com.pig4cloud.pig.common.security.service.PigUser; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -26,8 +27,11 @@ import org.springframework.security.oauth2.provider.token.UserAuthenticationConv import org.springframework.util.StringUtils; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Set; /** * @author lengleng @@ -69,7 +73,17 @@ public class PigUserAuthenticationConverter implements UserAuthenticationConvert String username = (String) map.get(SecurityConstants.DETAILS_USERNAME); Integer id = (Integer) map.get(SecurityConstants.DETAILS_USER_ID); Integer deptId = (Integer) map.get(SecurityConstants.DETAILS_DEPT_ID); - PigUser user = new PigUser(id, deptId, username, N_A, true, true, true, true, authorities); + + UserDataScope userDataScope = new UserDataScope(); + Object value = map.get(SecurityConstants.DETAILS_USER_DATA_SCOPE); + if (value != null) { + Map userDataScopeMap = (Map) value; + userDataScope.setAllScope((boolean) userDataScopeMap.get("allScope")); + userDataScope.setOnlySelf((boolean) userDataScopeMap.get("onlySelf")); + userDataScope.setScopeUserIds(new HashSet<>((List) userDataScopeMap.get("scopeUserIds"))); + userDataScope.setScopeDeptIds(new HashSet<>((List) userDataScopeMap.get("scopeDeptIds"))); + } + PigUser user = new PigUser(id, deptId, username, N_A, true, true, true, true, authorities, userDataScope); return new UsernamePasswordAuthenticationToken(user, N_A, authorities); } return null; diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/DataScopeProcessor.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/DataScopeProcessor.java new file mode 100644 index 00000000..3f6d09ac --- /dev/null +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/DataScopeProcessor.java @@ -0,0 +1,21 @@ +package com.pig4cloud.pig.common.security.datascope; + +import com.pig4cloud.pig.admin.api.entity.SysRole; +import com.pig4cloud.pig.admin.api.entity.SysUser; + +import java.util.List; + +/** + * @author hccake + */ +public interface DataScopeProcessor { + + /** + * 根据用户和角色信息,合并用户最终的数据权限 + * @param user 用户 + * @param roles 角色列表 + * @return UserDataScope + */ + UserDataScope mergeScopeType(SysUser user, List roles); + +} diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/PigDataScopeProcessor.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/PigDataScopeProcessor.java new file mode 100644 index 00000000..08825dc5 --- /dev/null +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/PigDataScopeProcessor.java @@ -0,0 +1,119 @@ +package com.pig4cloud.pig.common.security.datascope; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pig.admin.api.entity.SysRole; +import com.pig4cloud.pig.admin.api.entity.SysUser; +import com.pig4cloud.pig.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pig.admin.api.feign.RemoteUserService; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.core.constant.enums.DataScopeTypeEnum; +import com.pig4cloud.pig.common.core.util.R; +import lombok.RequiredArgsConstructor; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author hccake + */ +@RequiredArgsConstructor +public class PigDataScopeProcessor implements DataScopeProcessor { + + private final RemoteDeptService remoteDeptService; + + private final RemoteUserService remoteUserService; + + /** + * 合并角色的数据权限类型,排除相同的权限后,大的权限覆盖小的 + * @param user 用户 + * @param roles 角色列表 + * @return List 合并后的权限 + */ + @Override + public UserDataScope mergeScopeType(SysUser user, List roles) { + UserDataScope userDataScope = new UserDataScope(); + Set scopeUserIds = userDataScope.getScopeUserIds(); + Set scopeDeptIds = userDataScope.getScopeDeptIds(); + + // 任何用户都应该可以看到自己的数据 + Integer userId = user.getUserId(); + scopeUserIds.add(userId); + + if (CollectionUtil.isEmpty(roles)) { + return userDataScope; + } + + // 根据角色的权限返回进行分组 + Map> map = roles.stream().collect(Collectors.groupingBy(SysRole::getScopeType)); + + // 如果有全部权限,直接返回 + if (map.containsKey(DataScopeTypeEnum.ALL.getType())) { + userDataScope.setAllScope(true); + return userDataScope; + } + + // 如果有本级及子级,删除其包含的几类数据权限 + boolean hasLevelChildLevel = map.containsKey(DataScopeTypeEnum.LEVEL_CHILD_LEVEL.getType()); + if (hasLevelChildLevel) { + map.remove(DataScopeTypeEnum.SELF.getType()); + map.remove(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType()); + map.remove(DataScopeTypeEnum.LEVEL.getType()); + } + + // 是否有本人及子级权限 + boolean hasSelfChildLevel = map.containsKey(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType()); + // 是否有本级权限 + boolean hasLevel = map.containsKey(DataScopeTypeEnum.LEVEL.getType()); + if (hasSelfChildLevel || hasLevel) { + // 如果有本人及子级或者本级,都删除本人的数据权限 + map.remove(DataScopeTypeEnum.SELF.getType()); + // 如果同时拥有,则等于本级及子级权限 + if (hasSelfChildLevel && hasLevel) { + map.remove(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType()); + map.remove(DataScopeTypeEnum.LEVEL.getType()); + map.put(DataScopeTypeEnum.LEVEL_CHILD_LEVEL.getType(), new ArrayList<>()); + } + } + + // 这时如果仅仅只能看个人的,直接返回 + if (map.size() == 1 && map.containsKey(DataScopeTypeEnum.SELF.getType())) { + userDataScope.setOnlySelf(true); + return userDataScope; + } + + // 如果有 本级及子级 或者 本级,都把自己的 deptId 加进去 + Integer deptId = user.getDeptId(); + if (hasLevelChildLevel || hasLevel) { + scopeDeptIds.add(deptId); + } + // 如果有 本级及子级 或者 本人及子级,都把下级组织的 deptId 加进去 + if (hasLevelChildLevel || hasSelfChildLevel) { + List childDeptIdList = remoteDeptService.listChildDeptId(deptId, SecurityConstants.FROM_IN) + .getData(); + if (CollectionUtil.isNotEmpty(childDeptIdList)) { + scopeDeptIds.addAll(childDeptIdList); + } + } + // 自定义部门 + List sysRoles = map.get(DataScopeTypeEnum.CUSTOM.getType()); + if (CollectionUtil.isNotEmpty(sysRoles)) { + Set customDeptIds = sysRoles.stream().map(SysRole::getScopeResources).filter(Objects::nonNull) + .flatMap(x -> Arrays.stream(x.split(StrUtil.COMMA))).map(Integer::parseInt) + .collect(Collectors.toSet()); + scopeDeptIds.addAll(customDeptIds); + } + + // 把部门对应的用户id都放入集合中 + if (CollectionUtil.isNotEmpty(scopeDeptIds)) { + R> r = remoteUserService.listUserIdByDeptIds(scopeDeptIds, SecurityConstants.FROM_IN); + List userIds = r.getData(); + if (CollectionUtil.isNotEmpty(userIds)) { + scopeUserIds.addAll(userIds); + } + } + + return userDataScope; + } + +} diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/UserDataScope.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/UserDataScope.java new file mode 100644 index 00000000..98b333f6 --- /dev/null +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/datascope/UserDataScope.java @@ -0,0 +1,35 @@ +package com.pig4cloud.pig.common.security.datascope; + +import lombok.Data; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * @author hccake + */ +@Data +public class UserDataScope implements Serializable { + + /** + * 是否是全部数据权限 + */ + private boolean allScope = false; + + /** + * 是否仅能看自己 + */ + private boolean onlySelf = false; + + /** + * 数据权限范围,用户所能查看的用户id 集合 + */ + private Set scopeUserIds = new HashSet<>(); + + /** + * 数据权限范围,用户所能查看的部门id 集合 + */ + private Set scopeDeptIds = new HashSet<>(); + +} diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUser.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUser.java index ea635bfd..c3b9863f 100755 --- a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUser.java +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUser.java @@ -16,6 +16,7 @@ package com.pig4cloud.pig.common.security.service; +import com.pig4cloud.pig.common.security.datascope.UserDataScope; import lombok.Getter; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.core.GrantedAuthority; @@ -33,13 +34,19 @@ public class PigUser extends User { * 用户ID */ @Getter - private Integer id; + private final Integer id; /** * 部门ID */ @Getter - private Integer deptId; + private final Integer deptId; + + /** + * 用户数据权限信息 + */ + @Getter + private final UserDataScope userDataScope; /** * Construct the User with the details required by @@ -62,10 +69,11 @@ public class PigUser extends User { */ public PigUser(Integer id, Integer deptId, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, - Collection authorities) { + Collection authorities, UserDataScope userDataScope) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); this.id = id; this.deptId = deptId; + this.userDataScope = userDataScope; } } diff --git a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUserDetailsServiceImpl.java b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUserDetailsServiceImpl.java index 890324e3..442c8ac1 100755 --- a/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUserDetailsServiceImpl.java +++ b/pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/service/PigUserDetailsServiceImpl.java @@ -25,6 +25,8 @@ import com.pig4cloud.pig.common.core.constant.CacheConstants; import com.pig4cloud.pig.common.core.constant.CommonConstants; import com.pig4cloud.pig.common.core.constant.SecurityConstants; import com.pig4cloud.pig.common.core.util.R; +import com.pig4cloud.pig.common.security.datascope.DataScopeProcessor; +import com.pig4cloud.pig.common.security.datascope.UserDataScope; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -45,7 +47,7 @@ import java.util.Set; /** * 用户详细信息 * - * @author lengleng + * @author lengleng hccake */ @Slf4j @Service @@ -56,6 +58,8 @@ public class PigUserDetailsServiceImpl implements UserDetailsService { private final CacheManager cacheManager; + private final DataScopeProcessor dataScopeProcessor; + /** * 用户密码登录 * @param username 用户名 @@ -80,7 +84,7 @@ public class PigUserDetailsServiceImpl implements UserDetailsService { /** * 构建userdetails * @param result 用户信息 - * @return + * @return UserDetails */ private UserDetails getUserDetails(R result) { if (result == null || result.getData() == null) { @@ -100,10 +104,14 @@ public class PigUserDetailsServiceImpl implements UserDetailsService { .createAuthorityList(dbAuthsSet.toArray(new String[0])); SysUser user = info.getSysUser(); + // 数据权限填充 + UserDataScope userDataScope = dataScopeProcessor.mergeScopeType(user, info.getRoleList()); + // 构造security用户 return new PigUser(user.getUserId(), user.getDeptId(), user.getUsername(), SecurityConstants.BCRYPT + user.getPassword(), - StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL), true, true, true, authorities); + StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL), true, true, true, authorities, + userDataScope); } } diff --git a/pig-common/pig-common-security/src/main/resources/META-INF/spring.factories b/pig-common/pig-common-security/src/main/resources/META-INF/spring.factories index ba4694e7..42f2a723 100755 --- a/pig-common/pig-common-security/src/main/resources/META-INF/spring.factories +++ b/pig-common/pig-common-security/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.pig4cloud.pig.common.security.service.PigUserDetailsServiceImpl,\ - com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect + com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect,\ + com.pig4cloud.pig.common.security.datascope.PigDataScopeProcessor diff --git a/pig-common/pom.xml b/pig-common/pom.xml index 4ce58afd..62ae451e 100755 --- a/pig-common/pom.xml +++ b/pig-common/pom.xml @@ -40,5 +40,6 @@ pig-common-feign pig-common-swagger pig-common-test - + pig-common-datascope + diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/dto/UserInfo.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/dto/UserInfo.java index 1c76397f..c466d27b 100644 --- a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/dto/UserInfo.java +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/dto/UserInfo.java @@ -16,10 +16,14 @@ package com.pig4cloud.pig.admin.api.dto; +import com.pig4cloud.pig.admin.api.entity.SysMenu; +import com.pig4cloud.pig.admin.api.entity.SysRole; import com.pig4cloud.pig.admin.api.entity.SysUser; import lombok.Data; import java.io.Serializable; +import java.util.List; +import java.util.Set; /** * @author lengleng @@ -46,4 +50,9 @@ public class UserInfo implements Serializable { */ private Integer[] roles; + /** + * 角色集合 + */ + private List roleList; + } diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/SysRole.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/SysRole.java index 50b2c6d1..3d9a8fab 100644 --- a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/SysRole.java +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/entity/SysRole.java @@ -25,6 +25,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; /** *

@@ -56,6 +57,13 @@ public class SysRole extends BaseEntity { @ApiModelProperty(value = "角色描述") private String roleDesc; + @NotNull(message = "数据范围类型 不能为null") + @ApiModelProperty(value = "数据范围类型") + private Integer scopeType; + + @ApiModelProperty(value = "数据范围资源") + private String scopeResources; + /** * 删除标识(0-正常,1-删除) */ diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteDeptService.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteDeptService.java new file mode 100644 index 00000000..12f91bf0 --- /dev/null +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteDeptService.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.admin.api.feign; + +import com.pig4cloud.pig.admin.api.feign.factory.RemoteDeptServiceFallbackFactory; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.core.constant.ServiceNameConstants; +import com.pig4cloud.pig.common.core.util.R; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; + +import java.util.List; + +/** + * @author hccake + */ +@FeignClient(contextId = "remoteDeptService", value = ServiceNameConstants.UMPS_SERVICE, + fallbackFactory = RemoteDeptServiceFallbackFactory.class) +public interface RemoteDeptService { + + /** + * 查收子级id列表 + * @return 返回子级id列表 + */ + @GetMapping("/dept/child-id/{deptId}") + R> listChildDeptId(@PathVariable("deptId") Integer deptId, + @RequestHeader(SecurityConstants.FROM) String from); + +} diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteUserService.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteUserService.java index 30d5926e..eb00dece 100755 --- a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteUserService.java +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/RemoteUserService.java @@ -25,6 +25,10 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; +import java.util.Set; /** * @author lengleng @@ -51,4 +55,14 @@ public interface RemoteUserService { @GetMapping("/social/info/{inStr}") R social(@PathVariable("inStr") String inStr); + /** + * 根据部门id,查询对应的用户 id 集合 + * @param deptIds 部门id 集合 + * @param from 调用标志 + * @return 用户 id 集合 + */ + @GetMapping("/user/ids") + R> listUserIdByDeptIds(@RequestParam("deptIds") Set deptIds, + @RequestHeader(SecurityConstants.FROM) String from); + } diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/factory/RemoteDeptServiceFallbackFactory.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/factory/RemoteDeptServiceFallbackFactory.java new file mode 100644 index 00000000..14ab4c78 --- /dev/null +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/factory/RemoteDeptServiceFallbackFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.admin.api.feign.factory; + +import com.pig4cloud.pig.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pig.admin.api.feign.fallback.RemoteDeptServiceFallbackImpl; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * @author hccake + */ +@Component +public class RemoteDeptServiceFallbackFactory implements FallbackFactory { + + @Override + public RemoteDeptService create(Throwable throwable) { + RemoteDeptServiceFallbackImpl remoteDeptServiceFallback = new RemoteDeptServiceFallbackImpl(); + remoteDeptServiceFallback.setCause(throwable); + return remoteDeptServiceFallback; + } + +} diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteDeptServiceFallbackImpl.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteDeptServiceFallbackImpl.java new file mode 100644 index 00000000..bed2d8cd --- /dev/null +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteDeptServiceFallbackImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.admin.api.feign.fallback; + +import com.pig4cloud.pig.admin.api.dto.UserInfo; +import com.pig4cloud.pig.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pig.admin.api.feign.RemoteUserService; +import com.pig4cloud.pig.common.core.util.R; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author hccake + */ +@Slf4j +@Component +public class RemoteDeptServiceFallbackImpl implements RemoteDeptService { + + @Setter + private Throwable cause; + + @Override + public R> listChildDeptId(Integer deptId, String from) { + log.error("[listChildDeptId] feign 查询子级部门id列表失败", cause); + return null; + } + +} diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteUserServiceFallbackImpl.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteUserServiceFallbackImpl.java index db12847b..389a6dd5 100755 --- a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteUserServiceFallbackImpl.java +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/feign/fallback/RemoteUserServiceFallbackImpl.java @@ -23,6 +23,9 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.Set; + /** * @author lengleng * @date 2019/2/1 @@ -57,4 +60,10 @@ public class RemoteUserServiceFallbackImpl implements RemoteUserService { return null; } + @Override + public R> listUserIdByDeptIds(Set deptIds, String from) { + log.error("feign 根据部门ids查询用户Id集合失败:{}", deptIds, cause); + return null; + } + } diff --git a/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/UserInfoVO.java b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/UserInfoVO.java new file mode 100644 index 00000000..ab1451ce --- /dev/null +++ b/pig-upms/pig-upms-api/src/main/java/com/pig4cloud/pig/admin/api/vo/UserInfoVO.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.admin.api.vo; + +import com.pig4cloud.pig.admin.api.entity.SysUser; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author lengleng + * @date 2019/2/1 + *

+ * commit('SET_ROLES', data) commit('SET_NAME', data) commit('SET_AVATAR', data) + * commit('SET_INTRODUCTION', data) commit('SET_PERMISSIONS', data) + */ +@Data +public class UserInfoVO implements Serializable { + + /** + * 用户基本信息 + */ + private SysUser sysUser; + + /** + * 权限标识集合 + */ + private String[] permissions; + + /** + * 角色集合 + */ + private Integer[] roles; + +} diff --git a/pig-upms/pig-upms-api/src/main/resources/META-INF/spring.factories b/pig-upms/pig-upms-api/src/main/resources/META-INF/spring.factories index 8ed59881..a948b89e 100755 --- a/pig-upms/pig-upms-api/src/main/resources/META-INF/spring.factories +++ b/pig-upms/pig-upms-api/src/main/resources/META-INF/spring.factories @@ -2,6 +2,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.pig4cloud.pig.admin.api.feign.fallback.RemoteUserServiceFallbackImpl,\ com.pig4cloud.pig.admin.api.feign.fallback.RemoteLogServiceFallbackImpl,\ com.pig4cloud.pig.admin.api.feign.fallback.RemoteTokenServiceFallbackImpl,\ + com.pig4cloud.pig.admin.api.feign.fallback.RemoteDeptServiceFallbackImpl,\ com.pig4cloud.pig.admin.api.feign.factory.RemoteUserServiceFallbackFactory,\ com.pig4cloud.pig.admin.api.feign.factory.RemoteLogServiceFallbackFactory,\ - com.pig4cloud.pig.admin.api.feign.factory.RemoteTokenServiceFallbackFactory + com.pig4cloud.pig.admin.api.feign.factory.RemoteTokenServiceFallbackFactory,\ + com.pig4cloud.pig.admin.api.feign.factory.RemoteDeptServiceFallbackFactory diff --git a/pig-upms/pig-upms-biz/pom.xml b/pig-upms/pig-upms-biz/pom.xml index d2d08fc5..ccc15614 100644 --- a/pig-upms/pig-upms-biz/pom.xml +++ b/pig-upms/pig-upms-biz/pom.xml @@ -60,6 +60,11 @@ com.pig4cloud pig-common-mybatis + + + com.pig4cloud + pig-common-datascope + com.alibaba.cloud diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/DeptController.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/DeptController.java index e56bc548..9dd62e37 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/DeptController.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/DeptController.java @@ -20,6 +20,7 @@ import com.pig4cloud.pig.admin.api.entity.SysDept; import com.pig4cloud.pig.admin.service.SysDeptService; import com.pig4cloud.pig.common.core.util.R; import com.pig4cloud.pig.common.log.annotation.SysLog; +import com.pig4cloud.pig.common.security.annotation.Inner; import io.swagger.annotations.Api; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; @@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.time.LocalDateTime; +import java.util.List; /** *

@@ -121,4 +123,14 @@ public class DeptController { return R.ok(sysDeptService.getOne(new QueryWrapper<>(condition))); } + /** + * 查收子级id列表 + * @return 返回子级id列表 + */ + @Inner + @GetMapping(value = "/child-id/{deptId}") + public R> listChildDeptId(@PathVariable Integer deptId) { + return R.ok(sysDeptService.listChildDeptId(deptId)); + } + } diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/UserController.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/UserController.java index 8fd8fd32..ae191416 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/UserController.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/controller/UserController.java @@ -20,8 +20,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.pig4cloud.pig.admin.api.dto.UserDTO; +import com.pig4cloud.pig.admin.api.dto.UserInfo; import com.pig4cloud.pig.admin.api.entity.SysUser; import com.pig4cloud.pig.admin.api.vo.UserExcelVO; +import com.pig4cloud.pig.admin.api.vo.UserInfoVO; import com.pig4cloud.pig.admin.service.SysUserService; import com.pig4cloud.pig.common.core.util.R; import com.pig4cloud.pig.common.log.annotation.SysLog; @@ -33,10 +35,19 @@ import io.swagger.annotations.Api; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; import java.util.List; +import java.util.Set; /** * @author lengleng @@ -61,7 +72,12 @@ public class UserController { if (user == null) { return R.failed("获取当前用户信息失败"); } - return R.ok(userService.getUserInfo(user)); + UserInfo userInfo = userService.getUserInfo(user); + UserInfoVO vo = new UserInfoVO(); + vo.setSysUser(userInfo.getSysUser()); + vo.setRoles(userInfo.getRoles()); + vo.setPermissions(userInfo.getPermissions()); + return R.ok(vo); } /** @@ -78,6 +94,17 @@ public class UserController { return R.ok(userService.getUserInfo(user)); } + /** + * 根据部门id,查询对应的用户 id 集合 + * @param deptIds 部门id 集合 + * @return 用户 id 集合 + */ + @Inner + @GetMapping("/ids") + public R> listUserIdByDeptIds(@RequestParam("deptIds") Set deptIds) { + return R.ok(userService.listUserIdByDeptIds(deptIds)); + } + /** * 通过ID查询用户信息 * @param id ID diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/datascope/PigDataScope.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/datascope/PigDataScope.java new file mode 100644 index 00000000..93da1ae9 --- /dev/null +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/datascope/PigDataScope.java @@ -0,0 +1,115 @@ +package com.pig4cloud.pig.admin.datascope; + +import cn.hutool.core.collection.CollectionUtil; +import com.pig4cloud.pig.common.security.datascope.UserDataScope; +import com.pig4cloud.pig.common.security.service.PigUser; +import com.pig4cloud.pig.common.security.util.SecurityUtils; +import com.pigcloud.pig.common.datascope.DataScope; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.Parenthesis; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.schema.Column; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +/** + * 数据权限控制,要求表至少有个 user_id 字段 + * + * @author hccake + */ +@Component +public class PigDataScope implements DataScope { + + private static final String USER_ID = "user_id"; + + private static final String DEPT_ID = "dept_id"; + + /** + * 拥有 dept_id 字段的表名集合 + */ + private static final Set DEPT_ID_TABLE_NAMES = CollectionUtil.newHashSet("sys_user"); + + /** + * 仅拥有 user_id 字段的表名集合 + */ + private static final Set USER_ID_TABLE_NAMES = CollectionUtil.newHashSet(); + + private final Set tableNames; + + public PigDataScope() { + Set set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + set.addAll(DEPT_ID_TABLE_NAMES); + set.addAll(USER_ID_TABLE_NAMES); + this.tableNames = Collections.unmodifiableSet(set); + } + + @Override + public String getResource() { + return "userData"; + } + + @Override + public Collection getTableNames() { + return this.tableNames; + } + + @Override + public Expression getExpression(String tableName, Alias tableAlias) { + // 获取当前登录用户 + PigUser user = SecurityUtils.getUser(); + if (user == null) { + return null; + } + + UserDataScope userDataScope = user.getUserDataScope(); + + // 如果数据权限是全部,直接放行 + if (userDataScope.isAllScope()) { + return null; + } + + // 如果数据权限是仅自己 + if (userDataScope.isOnlySelf()) { + // 数据权限规则,where user_id = xx + return userIdEqualsToExpression(tableAlias, user.getId()); + } + + // 如果当前表有部门字段,则优先使用部门字段控制范围 + if (DEPT_ID_TABLE_NAMES.contains(tableName)) { + // 数据权限规则,where (user_id =xx or dept_id in ("x","y")) + EqualsTo equalsTo = userIdEqualsToExpression(tableAlias, user.getId()); + Expression inExpression = getInExpression(tableAlias, DEPT_ID, userDataScope.getScopeDeptIds()); + // 这里一定要加括号,否则如果有其他查询条件,or 会出问题 + return new Parenthesis(new OrExpression(equalsTo, inExpression)); + } + else { + // 数据权限规则,where user_id in ("x","y") + return getInExpression(tableAlias, USER_ID, userDataScope.getScopeUserIds()); + } + } + + private EqualsTo userIdEqualsToExpression(Alias tableAlias, Integer userId) { + Column column = new Column(tableAlias == null ? USER_ID : tableAlias.getName() + "." + USER_ID); + return new EqualsTo(column, new LongValue(userId)); + } + + private Expression getInExpression(Alias tableAlias, String columnName, Set scopeUserIds) { + Column column = new Column(tableAlias == null ? columnName : tableAlias.getName() + "." + columnName); + ExpressionList expressionList = new ExpressionList(); + List list = scopeUserIds.stream().map(LongValue::new).collect(Collectors.toList()); + expressionList.setExpressions(list); + return new InExpression(column, expressionList); + } + +} diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysDeptService.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysDeptService.java index 55f5e543..8329e9b9 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysDeptService.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysDeptService.java @@ -65,4 +65,11 @@ public interface SysDeptService extends IService { */ Boolean updateDeptById(SysDept sysDept); + /** + * 查找指定部门的子部门id列表 + * @param deptId 部门id + * @return List + */ + List listChildDeptId(Integer deptId); + } diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysUserService.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysUserService.java index f03d0b3a..799c0c80 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysUserService.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/SysUserService.java @@ -28,6 +28,7 @@ import com.pig4cloud.pig.common.core.util.R; import org.springframework.validation.BindingResult; import java.util.List; +import java.util.Set; /** * @author lengleng @@ -107,4 +108,11 @@ public interface SysUserService extends IService { */ R importUser(List excelVOList, BindingResult bindingResult); + /** + * 根据部门 id 列表查询对应的用户 id 集合 + * @param deptIds 部门 id 列表 + * @return userIdList + */ + List listUserIdByDeptIds(Set deptIds); + } diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysDeptServiceImpl.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysDeptServiceImpl.java index afc809f2..3b69aab4 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysDeptServiceImpl.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysDeptServiceImpl.java @@ -33,6 +33,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -108,6 +109,16 @@ public class SysDeptServiceImpl extends ServiceImpl impl return Boolean.TRUE; } + @Override + public List listChildDeptId(Integer deptId) { + List deptRelations = sysDeptRelationService.list(Wrappers.lambdaQuery() + .eq(SysDeptRelation::getAncestor, deptId).ne(SysDeptRelation::getDescendant, deptId)); + if (CollUtil.isNotEmpty(deptRelations)) { + return deptRelations.stream().map(SysDeptRelation::getDescendant).collect(Collectors.toList()); + } + return new ArrayList<>(); + } + /** * 查询全部部门树 * @return 树 diff --git a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysUserServiceImpl.java b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysUserServiceImpl.java index a8327551..b7116aac 100644 --- a/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysUserServiceImpl.java +++ b/pig-upms/pig-upms-biz/src/main/java/com/pig4cloud/pig/admin/service/impl/SysUserServiceImpl.java @@ -25,7 +25,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.pig4cloud.pig.admin.api.dto.UserDTO; import com.pig4cloud.pig.admin.api.dto.UserInfo; -import com.pig4cloud.pig.admin.api.entity.*; +import com.pig4cloud.pig.admin.api.entity.SysDept; +import com.pig4cloud.pig.admin.api.entity.SysMenu; +import com.pig4cloud.pig.admin.api.entity.SysRole; +import com.pig4cloud.pig.admin.api.entity.SysUser; +import com.pig4cloud.pig.admin.api.entity.SysUserRole; import com.pig4cloud.pig.admin.api.vo.UserExcelVO; import com.pig4cloud.pig.admin.api.vo.UserVO; import com.pig4cloud.pig.admin.mapper.SysDeptMapper; @@ -107,9 +111,11 @@ public class SysUserServiceImpl extends ServiceImpl impl public UserInfo getUserInfo(SysUser sysUser) { UserInfo userInfo = new UserInfo(); userInfo.setSysUser(sysUser); + // 设置角色列表 + List roleList = sysRoleMapper.listRolesByUserId(sysUser.getUserId()); + userInfo.setRoleList(roleList); // 设置角色列表 (ID) - List roleIds = sysRoleMapper.listRolesByUserId(sysUser.getUserId()).stream().map(SysRole::getRoleId) - .collect(Collectors.toList()); + List roleIds = roleList.stream().map(SysRole::getRoleId).collect(Collectors.toList()); userInfo.setRoles(ArrayUtil.toArray(roleIds, Integer.class)); // 设置权限列表(menu.permission) Set permissions = sysMenuService.findMenuByRoleId(CollUtil.join(roleIds, StrUtil.COMMA)).stream() @@ -117,6 +123,7 @@ public class SysUserServiceImpl extends ServiceImpl impl .filter(m -> StrUtil.isNotBlank(m.getPermission())).map(SysMenu::getPermission) .collect(Collectors.toSet()); userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class)); + return userInfo; } @@ -295,6 +302,13 @@ public class SysUserServiceImpl extends ServiceImpl impl return R.ok(); } + @Override + public List listUserIdByDeptIds(Set deptIds) { + return this.listObjs( + Wrappers.lambdaQuery(SysUser.class).select(SysUser::getUserId).in(SysUser::getDeptId, deptIds), + Integer.class::cast); + } + /** * 插入excel User */ diff --git a/pig-visual/pig-xxl-job-admin/pom.xml b/pig-visual/pig-xxl-job-admin/pom.xml index ae823f14..92afeea0 100644 --- a/pig-visual/pig-xxl-job-admin/pom.xml +++ b/pig-visual/pig-xxl-job-admin/pom.xml @@ -47,12 +47,6 @@ spring-boot-starter-mail - - - org.springframework.boot - spring-boot-starter-actuator - - org.mybatis.spring.boot