From 6cd9b3bf7e2432660b5e434bfb30ac1d64238b9f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 5 Dec 2021 10:44:17 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=A2=9E=E5=8A=A0=20Job=20=E7=9A=84?= =?UTF-8?q?=E5=A4=9A=E7=A7=9F=E6=88=B7=E7=9A=84=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../job/auth/SysUserSessionTimeoutJob.java | 2 + .../src/main/resources/application.yaml | 2 +- .../dal/dataobject/auth/SysUserSessionDO.java | 4 +- .../dal/dataobject/tenant/SysTenantDO.java | 45 ++++++++++++++ .../dal/mysql/tenant/SysTenantCoreMapper.java | 9 +++ .../service/tenant/SysTenantCoreService.java | 11 ++++ .../tenant/impl/SysTenantCoreServiceImpl.java | 29 ++++++++++ .../YudaoSecurityAutoConfiguration.java | 16 +++++ ...eadLocalSecurityContextHolderStrategy.java | 48 +++++++++++++++ .../core/util/SecurityFrameworkUtils.java | 3 +- .../yudao-spring-boot-starter-tenant/pom.xml | 6 ++ .../YudaoTenantJobAutoConfiguration.java | 41 +++++++++++++ .../framework/tenant/core/job/TenantJob.java | 14 +++++ .../core/job/TenantJobHandlerDecorator.java | 58 +++++++++++++++++++ .../core/job/TenantJobHandlerInvoker.java | 14 +++++ .../core/service/TenantFrameworkService.java | 19 ++++++ .../tenant/core/web/TenantWebFilter.java | 5 +- .../yudao/framework/tenant/package-info.java | 8 +-- .../main/resources/META-INF/spring.factories | 3 +- 19 files changed, 327 insertions(+), 10 deletions(-) create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/tenant/SysTenantDO.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/tenant/SysTenantCoreMapper.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/SysTenantCoreService.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/impl/SysTenantCoreServiceImpl.java create mode 100644 yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantJobAutoConfiguration.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerDecorator.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerInvoker.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/job/auth/SysUserSessionTimeoutJob.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/job/auth/SysUserSessionTimeoutJob.java index 4614ac619..57588fa80 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/job/auth/SysUserSessionTimeoutJob.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/job/auth/SysUserSessionTimeoutJob.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.system.job.auth; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -13,6 +14,7 @@ import javax.annotation.Resource; * @author 願 */ @Component +@TenantJob @Slf4j public class SysUserSessionTimeoutJob implements JobHandler { diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index f71a66fc6..6590d3c4c 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/yudao-admin-server/src/main/resources/application.yaml @@ -74,6 +74,6 @@ yudao: - cn.iocoder.yudao.adminserver.modules.infra.enums.InfErrorCodeConstants - cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants tenant: - tables: sys_user + tables: sys_user, sys_user_session debug: false diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/auth/SysUserSessionDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/auth/SysUserSessionDO.java index 67e4fc2d9..d30d38491 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/auth/SysUserSessionDO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/auth/SysUserSessionDO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.auth; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -25,7 +25,7 @@ import java.util.Date; @Data @Builder @EqualsAndHashCode(callSuper = true) -public class SysUserSessionDO extends BaseDO { +public class SysUserSessionDO extends TenantBaseDO { /** * 会话编号, 即 sessionId diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/tenant/SysTenantDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/tenant/SysTenantDO.java new file mode 100644 index 000000000..c0b5298c8 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/tenant/SysTenantDO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 租户 DO + * + * @author 芋道源码 + */ +@TableName(value = "sys_tenant", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SysTenantDO extends BaseDO { + + /** + * 租户编号,自增 + */ + private Long id; + /** + * 租户名,唯一 + */ + private String name; + /** + * 联系人 + */ + private String contactName; + /** + * 联系手机 + */ + private String contactMobile; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/tenant/SysTenantCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/tenant/SysTenantCoreMapper.java new file mode 100644 index 000000000..197f893fa --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/tenant/SysTenantCoreMapper.java @@ -0,0 +1,9 @@ +package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.tenant; + +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant.SysTenantDO; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SysTenantCoreMapper extends BaseMapperX { +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/SysTenantCoreService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/SysTenantCoreService.java new file mode 100644 index 000000000..f7bbab6ef --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/SysTenantCoreService.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.coreservice.modules.system.service.tenant; + +import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; + +/** + * 租户 Service 接口 + * + * @author 芋道源码 + */ +public interface SysTenantCoreService extends TenantFrameworkService { +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/impl/SysTenantCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/impl/SysTenantCoreServiceImpl.java new file mode 100644 index 000000000..046e4187c --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/tenant/impl/SysTenantCoreServiceImpl.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.coreservice.modules.system.service.tenant.impl; + +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.tenant.SysTenantDO; +import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.tenant.SysTenantCoreMapper; +import cn.iocoder.yudao.coreservice.modules.system.service.tenant.SysTenantCoreService; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 租户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class SysTenantCoreServiceImpl implements SysTenantCoreService { + + @Resource + private SysTenantCoreMapper tenantCoreMapper; + + @Override + public List getTenantIds() { + List tenants = tenantCoreMapper.selectList(); + return CollectionUtils.convertList(tenants, SysTenantDO::getId); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java index c0fa53ec7..684cfed9a 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java @@ -1,15 +1,18 @@ package cn.iocoder.yudao.framework.security.config; import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; +import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter; import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; @@ -85,4 +88,17 @@ public class YudaoSecurityAutoConfiguration { return new JWTAuthenticationTokenFilter(securityProperties, securityFrameworkService, globalExceptionHandler); } + /** + * 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法, + * 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略 + */ + @Bean + public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() { + MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean(); + methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class); + methodInvokingFactoryBean.setTargetMethod("setStrategyName"); + methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName()); + return methodInvokingFactoryBean; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java new file mode 100644 index 000000000..5e46daa1e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.framework.security.core.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.util.Assert; + +/** + * 基于 TransmittableThreadLocal 实现的 Security Context 持有者策略 + * 目的是,避免 @Async 等异步执行时,原生 ThreadLocal 的丢失问题 + * + * @author 芋道源码 + */ +public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { + + /** + * 使用 TransmittableThreadLocal 作为上下文 + */ + private static final ThreadLocal contextHolder = new TransmittableThreadLocal<>(); + + @Override + public void clearContext() { + contextHolder.remove(); + } + + @Override + public SecurityContext getContext() { + SecurityContext ctx = contextHolder.get(); + if (ctx == null) { + ctx = createEmptyContext(); + contextHolder.set(ctx); + } + return ctx; + } + + @Override + public void setContext(SecurityContext context) { + Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); + contextHolder.set(context); + } + + @Override + public SecurityContext createEmptyContext() { + return new SecurityContextImpl(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java index 5890e855a..f49f8faa5 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java @@ -95,13 +95,14 @@ public class SecurityFrameworkUtils { loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // 设置到上下文 - //何时调用 SecurityContextHolder.clearContext. spring security filter 应该会调用 clearContext SecurityContextHolder.getContext().setAuthentication(authenticationToken); // 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号; // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息 WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType()); // TODO @jason:使用 userId 会不会更合适哈? + // TODO @芋艿:activiti 需要使用 ttl 上下文 + // TODO @jason:清理问题 if (Objects.equals(UserTypeEnum.ADMIN.getValue(), loginUser.getUserType())) { org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername()); } diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml index 854db0bbf..6588eaf1b 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml @@ -32,6 +32,12 @@ cn.iocoder.boot yudao-spring-boot-starter-mybatis + + + + cn.iocoder.boot + yudao-spring-boot-starter-job + diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantJobAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantJobAutoConfiguration.java new file mode 100644 index 000000000..d9d2f3823 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantJobAutoConfiguration.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.framework.tenant.config; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator; +import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; + +/** + * 多租户针对 Job 的自动配置 + * + * @author 芋道源码 + */ +public class YudaoTenantJobAutoConfiguration { + + @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) { + return new BeanPostProcessor() { + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof JobHandler)) { + return bean; + } + // 有 TenantJob 注解的情况下,才会进行处理 + if (!AnnotationUtil.hasAnnotation(bean.getClass(), TenantJob.class)) { + return bean; + } + + // 使用 TenantJobHandlerDecorator 装饰 + return new TenantJobHandlerDecorator(tenantFrameworkService, (JobHandler) bean); + } + + }; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java new file mode 100644 index 000000000..fd2ecada9 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.framework.tenant.core.job; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 多租户 Job 注解 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TenantJob { +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerDecorator.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerDecorator.java new file mode 100644 index 000000000..25a6e016d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerDecorator.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.framework.tenant.core.job; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; +import lombok.AllArgsConstructor; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 多租户 JobHandler 装饰器 + * 任务执行时,会按照租户逐个执行 Job 的逻辑 + * + * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。 + * + * @author 芋道源码 + */ +@AllArgsConstructor +public class TenantJobHandlerDecorator implements JobHandler { + + private final TenantFrameworkService tenantFrameworkService; + /** + * 被装饰的 Job + */ + private final JobHandler jobHandler; + + @Override + public final String execute(String param) throws Exception { + // 获得租户列表 + List tenantIds = tenantFrameworkService.getTenantIds(); + if (CollUtil.isEmpty(tenantIds)) { + return null; + } + + // 逐个租户,执行 Job + Map results = new ConcurrentHashMap<>(); + tenantIds.parallelStream().forEach(tenantId -> { // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 + try { + // 设置租户 + TenantContextHolder.setTenantId(tenantId); + // 执行 Job + String result = jobHandler.execute(param); + // 添加结果 + results.put(tenantId, result); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + TenantContextHolder.clear(); + } + }); + return JsonUtils.toJsonString(results); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerInvoker.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerInvoker.java new file mode 100644 index 000000000..5127150a3 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerInvoker.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.framework.tenant.core.job; + +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker; + +/** + * 多租户 JobHandlerInvoker 拓展实现类 + * + * @author 芋道源码 + */ +public class TenantJobHandlerInvoker extends JobHandlerInvoker { + + + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java new file mode 100644 index 000000000..a36e612b6 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.framework.tenant.core.service; + +import java.util.List; + +/** + * Tenant 框架 Service 接口,定义获取租户信息 + * + * @author 芋道源码 + */ +public interface TenantFrameworkService { + + /** + * 获得所有租户 + * + * @return 租户编号数组 + */ + List getTenantIds(); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java index 027656476..40360e3fc 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java @@ -12,7 +12,10 @@ import java.io.IOException; /** * 多租户 Web 过滤器 - * 将请求 Header 中的 tenant-id 解析出来,添加到 {@link TenantContextHolder} 中,这样后续的 DB 等操作,可以获得到租户编号 + * 将请求 Header 中的 tenant-id 解析出来,添加到 {@link TenantContextHolder} 中,这样后续的 DB 等操作,可以获得到租户编号。 + * + * Q:会不会存在模拟 tenant-id 导致跨租户的问题? + * A:用户登陆后,获得的 Token 是基于租户级别隔离,从而保证授权失败。 * * @author 芋道源码 */ diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java index 1f39db41f..54a0e1ae6 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java @@ -1,8 +1,8 @@ /** * 多租户,支持如下层面: - * 1. DB:基于 MyBatis Plus 多租户的功能实现 - * 2. Job:TODO - * 3. MQ:TODO - * 4. Web:TODO + * 1. DB:基于 MyBatis Plus 多租户的功能实现。 + * 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。 + * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。 + * 4. MQ:TODO */ package cn.iocoder.yudao.framework.tenant; diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories index 6f0a6f12b..52ea04fb2 100644 --- a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories +++ b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\ - cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration + cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\ + cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration