From 0a76a6f526aa4d0da9f2699b6aac2c9bfaab8aaf Mon Sep 17 00:00:00 2001 From: Tang Cheng Date: Wed, 26 Jun 2019 22:29:48 +0800 Subject: [PATCH] =?utf8?q?=E7=A7=BB=E6=A4=8D=E5=A4=9A=E7=A7=9F=E6=88=B7?= =?utf8?q?=EF=BC=8C=E6=9C=AA=E5=AE=8C=E5=85=A8=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - config/application-devel-pg.properties | 8 -- .../com/supwisdom/dlpay/AppPreparedEvent.java | 2 +- .../framework/tenant/HibernateConfig.java | 63 ++++++++++++ .../MultiTenantConnectionProviderImpl.java | 63 ++++++++++-- .../tenant/MultiTenantIdentifierResolver.java | 22 +++-- .../{TenantThread.java => TenantContext.java} | 8 +- .../tenant/TenantDataSourceProvider.java | 96 ------------------- .../framework/tenant/TenantInterceptor.java | 28 ++++++ .../dlpay/framework/util/Constants.java | 2 +- .../com/supwisdom/dlpay/PayApiApplication.kt | 4 - .../controller/security_controller.kt | 2 +- .../com/supwisdom/dlpay/framework/tenant.kt | 24 +++++ .../kotlin/com/supwisdom/dlpay/security.kt | 2 +- src/main/resources/application.properties | 1 + src/main/resources/import.sql | 3 + 16 files changed, 194 insertions(+), 135 deletions(-) create mode 100644 src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java rename src/main/java/com/supwisdom/dlpay/framework/tenant/{TenantThread.java => TenantContext.java} (57%) delete mode 100644 src/main/java/com/supwisdom/dlpay/framework/tenant/TenantDataSourceProvider.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/tenant/TenantInterceptor.java create mode 100644 src/main/kotlin/com/supwisdom/dlpay/framework/tenant.kt diff --git a/build.gradle b/build.gradle index 96a615d4..7a92f849 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,6 @@ dependencies { implementation 'org.springframework.social:spring-social-web:1.1.6.RELEASE' implementation 'org.jetbrains.kotlin:kotlin-reflect' implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - implementation 'org.hibernate:hibernate-validator:5.4.1.Final' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties index fbe89a47..fa168072 100644 --- a/config/application-devel-pg.properties +++ b/config/application-devel-pg.properties @@ -31,12 +31,4 @@ shopbalance.updater.cron=- #spring.jpa.properties.hibernate.multiTenancy=DATABASE #spring.jpa.properties.hibernate.tenant_identifier_resolver=com.supwisdom.dlpay.framework.tenant.MultiTenantIdentifierResolver #spring.jpa.properties.hibernate.multi_tenant_connection_provider=com.supwisdom.dlpay.framework.tenant.MultiTenantConnectionProviderImpl -datasource.driverClass=org.postgresql.Driver -datasource.url=jdbc:postgresql://172.28.201.70:15432/payapi -datasource.username=payapi -datasource.password=123456 -datasource.maxActive=100 -datasource.maxIdle=30 -datasource.maxWait=30 -datasource.minIdle=5 ##################多租户配置 end################################ \ No newline at end of file diff --git a/src/main/java/com/supwisdom/dlpay/AppPreparedEvent.java b/src/main/java/com/supwisdom/dlpay/AppPreparedEvent.java index 304379c2..4dead6ae 100644 --- a/src/main/java/com/supwisdom/dlpay/AppPreparedEvent.java +++ b/src/main/java/com/supwisdom/dlpay/AppPreparedEvent.java @@ -6,7 +6,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; -@Configuration +//@Configuration public class AppPreparedEvent { private final DictPool dictPool; diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java new file mode 100644 index 00000000..947e0ab0 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/tenant/HibernateConfig.java @@ -0,0 +1,63 @@ +package com.supwisdom.dlpay.framework.tenant; + +import org.hibernate.MultiTenancyStrategy; +import org.hibernate.cfg.Environment; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class HibernateConfig { + private final JpaProperties jpaProperties; + + private final HibernateProperties hibernateProperties; + + public HibernateConfig(@Autowired JpaProperties jpaProperties, + HibernateProperties hibernateProperties) { + this.jpaProperties = jpaProperties; + this.hibernateProperties = hibernateProperties; + } + + @Bean + public JpaVendorAdapter getJpaVendorAdapter() { + return new HibernateJpaVendorAdapter(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource, + MultiTenantConnectionProvider multiTenantConnectionProvider, + CurrentTenantIdentifierResolver currentTenantIdentifierResolver) { + Map properties = new HashMap<>(); + properties.putAll(hibernateProperties + .determineHibernateProperties(jpaProperties.getProperties(), + new HibernateSettings())); + properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA); + properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider); + properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver); + + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource); + em.setPackagesToScan("com.supwisdom"); + em.setJpaPropertyMap(properties); + em.setJpaVendorAdapter(getJpaVendorAdapter()); + return em; + } + + @Bean + public EntityManagerFactory entityManagerFactoryBean(LocalContainerEntityManagerFactoryBean localBean) { + return localBean.getNativeEntityManagerFactory(); + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantConnectionProviderImpl.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantConnectionProviderImpl.java index 0f1d799f..7a0fb8a1 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantConnectionProviderImpl.java +++ b/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantConnectionProviderImpl.java @@ -1,20 +1,67 @@ package com.supwisdom.dlpay.framework.tenant; +import org.hibernate.HibernateException; import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; /** * Created by shuwei on 2018/12/4. */ -public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl { - @Override - protected DataSource selectAnyDataSource() { - return TenantDataSourceProvider.getTenantDataSource("default"); - } +@Component +public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider { + @Autowired + private DataSource dataSource; + + @Override + public Connection getAnyConnection() throws SQLException { + return dataSource.getConnection(); + } - @Override - protected DataSource selectDataSource(String tenantIdentifier) { - return TenantDataSourceProvider.getTenantDataSource(TenantThread.getTenantSchema()); + @Override + public void releaseAnyConnection(Connection connection) throws SQLException { + connection.close(); + } + + @Override + public Connection getConnection(String ti) throws SQLException { + String tenantIdentifier = TenantContext.getTenantSchema(); + final Connection connection = getAnyConnection(); + try { + if (tenantIdentifier != null) { + connection.createStatement().execute("SET search_path = " + tenantIdentifier + ", public"); + } else { + connection.createStatement().execute("SET search_path = public"); + } + } catch (SQLException e) { + throw new HibernateException("Problem setting schema to " + tenantIdentifier, e); } + return connection; + } + + @Override + public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { + connection.close(); + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @SuppressWarnings("rawtypes") + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return false; + } + + @Override + public T unwrap(Class unwrapType) { + return null; + } } diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantIdentifierResolver.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantIdentifierResolver.java index d2dc781f..c1dc432b 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantIdentifierResolver.java +++ b/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantIdentifierResolver.java @@ -1,21 +1,23 @@ package com.supwisdom.dlpay.framework.tenant; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; +import org.springframework.stereotype.Component; /** * Created by shuwei on 2018/12/4. */ +@Component public class MultiTenantIdentifierResolver implements CurrentTenantIdentifierResolver { - @Override - public String resolveCurrentTenantIdentifier() { - if(TenantThread.getTenantSchema()==null){ - return "default"; - } - return TenantThread.getTenantSchema(); + @Override + public String resolveCurrentTenantIdentifier() { + if (TenantContext.getTenantSchema() == null) { + return "default"; } + return TenantContext.getTenantSchema(); + } - @Override - public boolean validateExistingCurrentSessions() { - return true; - } + @Override + public boolean validateExistingCurrentSessions() { + return true; + } } diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantThread.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantContext.java similarity index 57% rename from src/main/java/com/supwisdom/dlpay/framework/tenant/TenantThread.java rename to src/main/java/com/supwisdom/dlpay/framework/tenant/TenantContext.java index 2bfb113d..bbee36ad 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantThread.java +++ b/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantContext.java @@ -3,14 +3,14 @@ package com.supwisdom.dlpay.framework.tenant; /** * Created by shuwei on 2018/11/29. */ -public class TenantThread { - private static ThreadLocal tenant_schema = new ThreadLocal<>(); +public class TenantContext { + private static ThreadLocal currentTenant = new ThreadLocal<>(); public static void setTenantSchema(String tid) { - tenant_schema.set(tid); + currentTenant.set(tid); } public static String getTenantSchema() { - return tenant_schema.get(); + return currentTenant.get(); } } diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantDataSourceProvider.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantDataSourceProvider.java deleted file mode 100644 index d93db46a..00000000 --- a/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantDataSourceProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.supwisdom.dlpay.framework.tenant; - -import org.apache.commons.dbcp.BasicDataSource; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.sql.DataSource; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by shuwei on 2018/12/4. - */ -@Component -public class TenantDataSourceProvider { - @Value("${datasource.driverClass}") - private String driver; - @Value("${datasource.url}") - private String url; - @Value("${datasource.username}") - private String username; - @Value("${datasource.password}") - private String password; - @Value("${datasource.maxIdle}") - private int maxIdle; - @Value("${datasource.maxActive}") - private int maxActive; - @Value("${datasource.minIdle}") - private int minIdle; - @Value("${datasource.maxWait}") - private int maxWait; - - public static String static_driver; - public static String static_url; - public static String static_username; - public static String static_password; - public static int static_maxIdle; - public static int static_maxActive; - public static int static_minIdle; - public static int static_maxWait; - - - @PostConstruct - public void beforeInit() { - static_driver = driver; - static_url = url; - static_username = username; - static_password = password; - static_maxIdle = maxIdle; - static_maxActive = maxActive; - static_minIdle = minIdle; - static_maxWait = maxWait; - } - // 使用一个map来存储我们租户和对应的数据源,租户和数据源的信息就是从我们的tenant_info表中读出来 - private static Map dataSourceMap = new HashMap<>(); - - /** - * 静态建立一个数据源,也就是我们的默认数据源,假如我们的访问信息里面没有指定tenantId,就使用默认数据源。 - * 在我这里默认数据源是cloud_config,实际上你可以指向你们的公共信息的库,或者拦截这个操作返回错误。 - */ - // 根据传进来的tenantId决定返回的数据源 - public static DataSource getTenantDataSource(String schemaId) { - DataSource d = dataSourceMap.get(schemaId); - if (d == null) { - String appSchema = schemaId != null ? "'" + schemaId + "','public'" : "'public'"; - String searchPath = "SET search_path = " + appSchema + ";"; - d = createDataSource(schemaId == null, searchPath); - dataSourceMap.put(schemaId, d); - } - return d; - } - - // 初始化的时候用于添加数据源的方法 - public static DataSource createDataSource(boolean rootdatabase, String searchPath) { - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setDriverClassName(static_driver); - dataSource.setUrl(static_url); - dataSource.setUsername(static_username); - dataSource.setPassword(static_password); - dataSource.setMaxActive(static_maxActive); - dataSource.setMaxIdle(static_maxIdle); - dataSource.setMaxWait(static_maxWait); - dataSource.setMinIdle(static_minIdle); - if(static_driver.toLowerCase().contains("oracle")){ - dataSource.setValidationQuery("select 1 from dual"); - }else { - dataSource.setValidationQuery("select 1 "); - if(static_driver.toLowerCase().contains("postgresql")) { - dataSource.setConnectionInitSqls(Collections.singletonList(searchPath)); - } - } - return dataSource; - } -} diff --git a/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantInterceptor.java b/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantInterceptor.java new file mode 100644 index 00000000..73bdbea6 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/tenant/TenantInterceptor.java @@ -0,0 +1,28 @@ +package com.supwisdom.dlpay.framework.tenant; + +import com.supwisdom.dlpay.framework.util.Constants; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class TenantInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String tenantId = request.getHeader(Constants.HEADER_TETANTID); + TenantContext.setTenantSchema(tenantId); + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, + Object handler, ModelAndView modelAndView) + throws Exception { + // + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/Constants.java b/src/main/java/com/supwisdom/dlpay/framework/util/Constants.java index c2e8555c..17dd6bc6 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/util/Constants.java +++ b/src/main/java/com/supwisdom/dlpay/framework/util/Constants.java @@ -2,7 +2,7 @@ package com.supwisdom.dlpay.framework.util; public class Constants { // HTTP HEADER define - public static final String HEADER_TETANTID = "TENANT-ID"; + public static final String HEADER_TETANTID = "X-TENANT-ID"; // define public static final String JWT_CLAIM_TENANTID = "tenantId"; diff --git a/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt index 3cfe7818..430a8bd4 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt @@ -1,15 +1,11 @@ package com.supwisdom.dlpay -import com.supwisdom.dlpay.system.common.DictPool import io.lettuce.core.ReadFrom -import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.context.event.ApplicationPreparedEvent import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.event.EventListener import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.connection.RedisPassword import org.springframework.data.redis.connection.RedisStandaloneConfiguration diff --git a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt index 2304b37a..393b5fbc 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt @@ -64,7 +64,7 @@ class ApiAuthController { @GetMapping(value = ["/gettoken", "/gettoken/{clientid}"]) fun loginInit(appid: String, @PathVariable clientid: String?, - @RequestHeader(Constants.HEADER_TETANTID) tetantId: String): ResponseEntity { + @RequestHeader(Constants.HEADER_TETANTID) tetantId: String?): ResponseEntity { tetantConfigDao.default?.also { if (it.tenantId != tetantId) { diff --git a/src/main/kotlin/com/supwisdom/dlpay/framework/tenant.kt b/src/main/kotlin/com/supwisdom/dlpay/framework/tenant.kt new file mode 100644 index 00000000..2990f19e --- /dev/null +++ b/src/main/kotlin/com/supwisdom/dlpay/framework/tenant.kt @@ -0,0 +1,24 @@ +package com.supwisdom.dlpay.framework + +import com.supwisdom.dlpay.framework.util.Constants +import org.springframework.core.annotation.Order +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse +import javax.servlet.annotation.WebFilter +import javax.servlet.http.HttpServletRequest + + +@Order(1) +@WebFilter(filterName = "multi_tenant_filter", urlPatterns = ["/*"]) +class TenantNameFilter : Filter { + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + if (request is HttpServletRequest) { + request.getHeader(Constants.HEADER_TETANTID)?.also { + // set tanent datasource + } + } + chain.doFilter(request, response) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt index 8cf8e9fc..d898fb7d 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/security.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt @@ -98,7 +98,7 @@ class ApiJwtAuthenticationFilter : OncePerRequestFilter() { return } val claims = getUtil().verifyToken(jwt) - if(url.equals("/mobileapi/logout")){ + if(url == "/mobileapi/logout"){ SecurityContextHolder.clearContext() apiJwtRepository.deleteById(claims[ReservedClaimNames.JWT_ID].toString()) throw JoseException("JWT has not been register") diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 18ea9d24..4beb006e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -14,6 +14,7 @@ logging.level.org.hibernate.SQL=debug #################### JSP PAGE #################### #spring.mvc.view.prefix=/pages/ #spring.mvc.view.suffix=.jsp +server.servlet.context-path=/payapi #################### thymeleaf #################### spring.mvc.static-path-pattern=/static/** spring.thymeleaf.prefix=classpath:/templates/ diff --git a/src/main/resources/import.sql b/src/main/resources/import.sql index 262c0c13..a1161f6f 100644 --- a/src/main/resources/import.sql +++ b/src/main/resources/import.sql @@ -1,3 +1,6 @@ +INSERT INTO tb_tenantconfig(cfgid, tenantid, datacenter_id) +values ('main', '10000110', '01'); + INSERT INTO tb_operator(operid, closedate, opendate, opercode, opername, operpwd, opertype, status) VALUES ('LOR2IwRkbOjp+sVG9KR2BpHZbwGKepS4', '20500101', '20190101', 'system', '系统管理员', '$2a$10$Ex9xp11.vCaD8D0a7ahiUOKqDij1TcCUBwRAmrqXeDvAkmzLibn4.', 'oper', 'normal'); -- 2.17.1