增加了multi-tenant-core, multi-tenant-datasource ,未完全测试
diff --git a/multi-tenant-core/build.gradle b/multi-tenant-core/build.gradle
new file mode 100644
index 0000000..b456c6e
--- /dev/null
+++ b/multi-tenant-core/build.gradle
@@ -0,0 +1,9 @@
+plugins {
+ id 'java'
+ id 'org.springframework.boot'
+}
+
+dependencies {
+ implementation "org.springframework:spring-webmvc"
+ implementation "javax.servlet:javax.servlet-api:${javaxServletVersion}"
+}
\ No newline at end of file
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java
new file mode 100644
index 0000000..3ea1eb1
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java
@@ -0,0 +1,40 @@
+package com.supwisdom.multitenant;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+
+public class MultiTenantAutoConfiguration {
+
+ @Configuration
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ @ComponentScan(basePackages = {"com.supwisdom.multitenant"})
+ public static class SessionTenantInterceptorAutoConfig {
+ }
+
+ // @ConditionalOnProperty(
+// name = "multi-tenant.interceptor.httpsession.enabled",
+// havingValue = "true")
+// @Bean
+// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+// @ConditionalOnBean({SessionTenantInterceptorAutoConfig.class})
+// public SessionTenantInterceptorAutoConfig createSessionInterceptor() {
+// return new SessionTenantInterceptorAutoConfig();
+// }
+
+ @Configuration
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ @ComponentScan(basePackages = {"com.supwisdom.multitenant"})
+ public static class HttpHeaderTenantInterceptorAutoConfig {
+ }
+
+// @Bean
+// @ConditionalOnProperty(
+// name = "multi-tenant.interceptor.httpheader.enabled",
+// havingValue = "true")
+// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+// public HttpHeaderTenantInterceptorAutoConfig createHttpHeaderInterceptor() {
+// return new HttpHeaderTenantInterceptorAutoConfig();
+// }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContext.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContext.java
new file mode 100644
index 0000000..0c3bbf2
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContext.java
@@ -0,0 +1,9 @@
+package com.supwisdom.multitenant;
+
+import java.io.Serializable;
+
+public interface TenantContext extends Serializable {
+ TenantDetails getTenant();
+
+ void setTenant(TenantDetails details);
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolder.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolder.java
new file mode 100644
index 0000000..bb0d51f
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolder.java
@@ -0,0 +1,24 @@
+package com.supwisdom.multitenant;
+
+import com.supwisdom.multitenant.impl.ThreadLocalTenantContextHolderStrategy;
+
+public class TenantContextHolder {
+ private static TenantContextHolderStrategy strategy;
+
+ static {
+ initialize();
+ }
+
+ private static void initialize() {
+ strategy = new ThreadLocalTenantContextHolderStrategy();
+ }
+
+ public static TenantContext getContext() {
+ return strategy.getContext();
+ }
+
+ public static void clearContext() {
+ strategy.clearContext();
+ }
+
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolderStrategy.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolderStrategy.java
new file mode 100644
index 0000000..300130a
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantContextHolderStrategy.java
@@ -0,0 +1,11 @@
+package com.supwisdom.multitenant;
+
+public interface TenantContextHolderStrategy {
+ TenantContext getContext();
+
+ void setContext(TenantContext tenant);
+
+ TenantContext createEmptyContext();
+
+ void clearContext();
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetails.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetails.java
new file mode 100644
index 0000000..07372bb
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetails.java
@@ -0,0 +1,46 @@
+package com.supwisdom.multitenant;
+
+import java.io.Serializable;
+
+public class TenantDetails implements Serializable {
+ private String id;
+
+ private String dbSchema;
+
+ private Boolean enabled;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getDbSchema() {
+ return dbSchema;
+ }
+
+ public void setDbSchema(String dbSchema) {
+ this.dbSchema = dbSchema;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TenantDetails) {
+ TenantDetails other = (TenantDetails) obj;
+ if (getId().equals(other.getId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantInterceptor.java
new file mode 100644
index 0000000..3a50249
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantInterceptor.java
@@ -0,0 +1,7 @@
+package com.supwisdom.multitenant;
+
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+public abstract class TenantInterceptor extends HandlerInterceptorAdapter {
+
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java
new file mode 100644
index 0000000..97f8625
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java
@@ -0,0 +1,13 @@
+package com.supwisdom.multitenant.annotations;
+
+import com.supwisdom.multitenant.MultiTenantAutoConfiguration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import(MultiTenantAutoConfiguration.HttpHeaderTenantInterceptorAutoConfig.class)
+@Documented
+public @interface EnableHttpHeaderTenantInterceptor {
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java
new file mode 100644
index 0000000..885140b
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java
@@ -0,0 +1,13 @@
+package com.supwisdom.multitenant.annotations;
+
+import com.supwisdom.multitenant.MultiTenantAutoConfiguration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import(MultiTenantAutoConfiguration.SessionTenantInterceptorAutoConfig.class)
+@Documented
+public @interface EnableSessionTenantInterceptor {
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java
new file mode 100644
index 0000000..b2d9585
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java
@@ -0,0 +1,23 @@
+package com.supwisdom.multitenant.config;
+
+
+import com.supwisdom.multitenant.impl.HttpHeaderTenantInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@ConditionalOnBean(value = {HttpHeaderTenantInterceptor.class})
+public class HttpHeaderWebMvcConfig implements WebMvcConfigurer {
+ private final HttpHeaderTenantInterceptor interceptor;
+
+ public HttpHeaderWebMvcConfig(HttpHeaderTenantInterceptor interceptor) {
+ this.interceptor = interceptor;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(interceptor);
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantCoreUtils.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantCoreUtils.java
new file mode 100644
index 0000000..97c7096
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantCoreUtils.java
@@ -0,0 +1,6 @@
+package com.supwisdom.multitenant.config;
+
+public final class TenantCoreUtils {
+ public static final String INTERNAL_TENANT_INTERCEPTOR_BEAN_NAME = "coms.supwisdom.multitenant.interceptor";
+
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantHeaderProperties.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantHeaderProperties.java
new file mode 100644
index 0000000..4d31390
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantHeaderProperties.java
@@ -0,0 +1,20 @@
+package com.supwisdom.multitenant.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties("multi-tenant.header")
+public class TenantHeaderProperties {
+ @Value("${key:X-Tenant-Id}")
+ private String key;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantJwtProperties.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantJwtProperties.java
new file mode 100644
index 0000000..c4dcbb4
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantJwtProperties.java
@@ -0,0 +1,31 @@
+package com.supwisdom.multitenant.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties("multi-tenant.jwt")
+public class TenantJwtProperties {
+ @Value("${header:Authentication}")
+ private String jwtHeader;
+
+ @Value("${schema:Bears}")
+ private String schema;
+
+ public String getJwtHeader() {
+ return jwtHeader;
+ }
+
+ public void setJwtHeader(String jwtHeader) {
+ this.jwtHeader = jwtHeader;
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public void setSchema(String schema) {
+ this.schema = schema;
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantSessionProperties.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantSessionProperties.java
new file mode 100644
index 0000000..3d57c62
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/TenantSessionProperties.java
@@ -0,0 +1,20 @@
+package com.supwisdom.multitenant.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties("multi-tenant.session")
+public class TenantSessionProperties {
+ @Value("${name:multi-tenant-id}")
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java
new file mode 100644
index 0000000..b8ae342
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java
@@ -0,0 +1,47 @@
+package com.supwisdom.multitenant.impl;
+
+import com.supwisdom.multitenant.*;
+import com.supwisdom.multitenant.config.TenantHeaderProperties;
+import com.supwisdom.multitenant.config.TenantJwtProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+@ConditionalOnBean(MultiTenantAutoConfiguration.HttpHeaderTenantInterceptorAutoConfig.class)
+public class HttpHeaderTenantInterceptor extends TenantInterceptor {
+ private final TenantJwtProperties properties;
+
+ private final TenantHeaderProperties headerProperties;
+
+ public HttpHeaderTenantInterceptor(TenantJwtProperties properties,
+ TenantHeaderProperties headerProperties) {
+ this.properties = properties;
+ this.headerProperties = headerProperties;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ String tenant = request.getHeader(headerProperties.getKey());
+ if (tenant != null && !StringUtils.isEmpty(tenant)) {
+ TenantDetails details = new TenantDetails();
+ details.setId(tenant);
+ TenantContextHolder.getContext().setTenant(details);
+ }
+ String header = request.getHeader(properties.getJwtHeader());
+ if (header.startsWith(properties.getSchema())) {
+ // parse jwt
+ }
+ return super.preHandle(request, response, handler);
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+ ModelAndView modelAndView) throws Exception {
+ TenantContextHolder.clearContext();
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java
new file mode 100644
index 0000000..3f9ae2c
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java
@@ -0,0 +1,39 @@
+package com.supwisdom.multitenant.impl;
+
+import com.supwisdom.multitenant.*;
+import com.supwisdom.multitenant.config.TenantSessionProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+@ConditionalOnBean(MultiTenantAutoConfiguration.SessionTenantInterceptorAutoConfig.class)
+public class SessionTenantInterceptor extends TenantInterceptor {
+ private final TenantSessionProperties properties;
+
+ public SessionTenantInterceptor(TenantSessionProperties properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ String tenant = (String) request.getSession().getAttribute(properties.getName());
+ if (tenant != null && !StringUtils.isEmpty(tenant)) {
+ TenantDetails details = new TenantDetails();
+ details.setId(tenant);
+ TenantContextHolder.getContext().setTenant(details);
+ }
+ return super.preHandle(request, response, handler);
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+ ModelAndView modelAndView) throws Exception {
+ TenantContextHolder.clearContext();
+ super.postHandle(request, response, handler, modelAndView);
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/TenantContextImpl.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/TenantContextImpl.java
new file mode 100644
index 0000000..39bdae8
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/TenantContextImpl.java
@@ -0,0 +1,27 @@
+package com.supwisdom.multitenant.impl;
+
+import com.supwisdom.multitenant.TenantContext;
+import com.supwisdom.multitenant.TenantDetails;
+
+public class TenantContextImpl implements TenantContext {
+ private TenantDetails details;
+
+ @Override
+ public TenantDetails getTenant() {
+ return details;
+ }
+
+ @Override
+ public void setTenant(TenantDetails details) {
+ this.details = details;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TenantContextImpl) {
+ TenantContextImpl other = (TenantContextImpl) obj;
+ getTenant().equals(other.getTenant());
+ }
+ return false;
+ }
+}
diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/ThreadLocalTenantContextHolderStrategy.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/ThreadLocalTenantContextHolderStrategy.java
new file mode 100644
index 0000000..fd325ec
--- /dev/null
+++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/ThreadLocalTenantContextHolderStrategy.java
@@ -0,0 +1,33 @@
+package com.supwisdom.multitenant.impl;
+
+import com.supwisdom.multitenant.TenantContext;
+import com.supwisdom.multitenant.TenantContextHolderStrategy;
+
+public class ThreadLocalTenantContextHolderStrategy implements TenantContextHolderStrategy {
+ private static final ThreadLocal<TenantContext> contextHolder = new ThreadLocal<>();
+
+ @Override
+ public TenantContext getContext() {
+ TenantContext context = contextHolder.get();
+ if (context == null) {
+ context = createEmptyContext();
+ contextHolder.set(context);
+ }
+ return context;
+ }
+
+ @Override
+ public void setContext(TenantContext tenant) {
+ contextHolder.set(tenant);
+ }
+
+ @Override
+ public void clearContext() {
+ contextHolder.remove();
+ }
+
+ @Override
+ public TenantContext createEmptyContext() {
+ return new TenantContextImpl();
+ }
+}
diff --git a/multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java
new file mode 100644
index 0000000..4286f3c
--- /dev/null
+++ b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java
@@ -0,0 +1,33 @@
+package com.supwisdom.multitenant;
+
+import com.supwisdom.multitenant.annotations.EnableSessionTenantInterceptor;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.TestPropertySource;
+
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+
+@SpringBootTest(classes = {AutoConfigurationTest.class})
+@EnableSessionTenantInterceptor
+@TestPropertySource("/test.properties")
+public class AutoConfigurationTest {
+
+ @Autowired
+ private ApplicationContext appContext;
+
+ @Test
+ public void testSessionOk() {
+ String[] beans = appContext.getBeanDefinitionNames();
+ Arrays.sort(beans);
+ for (String bean : beans) {
+ System.out.println(bean);
+ }
+ assertThat(TenantContextHolder.getContext(), notNullValue());
+ }
+
+}
diff --git a/multi-tenant-core/src/test/resources/test.properties b/multi-tenant-core/src/test/resources/test.properties
new file mode 100644
index 0000000..80c6377
--- /dev/null
+++ b/multi-tenant-core/src/test/resources/test.properties
@@ -0,0 +1 @@
+debug=true
\ No newline at end of file