未验证 提交 9f275dd8 编写于 作者: zlt2000's avatar zlt2000 提交者: Gitee

!24 dev

Merge pull request !24 from zlt2000/dev
# zlt-microservices-platform
<p align="center">
<img src="https://img.shields.io/badge/Version-5.2.0-critical" alt="Downloads"/>
<img src="https://img.shields.io/badge/Spring%20Boot-2.5.7-blue" alt="Downloads"/>
<img src="https://img.shields.io/badge/Spring%20Cloud-2020.0.4-blue" alt="Downloads"/>
<img src="https://img.shields.io/badge/Version-5.3.0-critical" alt="Downloads"/>
<img src="https://img.shields.io/badge/Spring%20Boot-2.5.13-blue" alt="Downloads"/>
<img src="https://img.shields.io/badge/Spring%20Cloud-2020.0.5-blue" alt="Downloads"/>
<img src="https://img.shields.io/badge/Spring%20Cloud%20Alibaba-2021.1-blue" alt="Downloads"/>
<img src="https://img.shields.io/badge/Elasticsearch-7.x-brightgreen" alt="Downloads"/>
<a target="_blank" href='https://gitee.com/zlt2000/microservices-platform'>
......
......@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
<packaging>pom</packaging>
<properties>
......@@ -14,8 +14,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>8</java.version>
<spring-cloud-alibaba-dependencies.version>2021.1</spring-cloud-alibaba-dependencies.version>
<spring-boot-dependencies.version>2.5.9</spring-boot-dependencies.version>
<spring-cloud-dependencies.version>2020.0.4</spring-cloud-dependencies.version>
<spring-boot-dependencies.version>2.5.13</spring-boot-dependencies.version>
<spring-cloud-dependencies.version>2020.0.5</spring-cloud-dependencies.version>
<commons-collections4.version>4.4</commons-collections4.version>
<swagger.butler.version>2.0.1</swagger.butler.version>
<jjwt.version>0.9.1</jjwt.version>
......@@ -27,12 +27,12 @@
<redisson-starter.version>3.16.1</redisson-starter.version>
<easyCaptcha.version>1.6.2</easyCaptcha.version>
<hutool.version>5.7.20</hutool.version>
<mybatis-plus-boot-starter.version>3.4.0</mybatis-plus-boot-starter.version>
<mybatis-plus-boot-starter.version>3.5.1</mybatis-plus-boot-starter.version>
<aliyun-sdk-oss>3.8.1</aliyun-sdk-oss>
<qiniu-java-sdk>7.2.28</qiniu-java-sdk>
<easypoi.version>4.1.3</easypoi.version>
<poi.version>4.1.1</poi.version>
<spring-boot-admin.version>2.5.5</spring-boot-admin.version>
<spring-boot-admin.version>2.5.6</spring-boot-admin.version>
<velocity.version>1.7</velocity.version>
<commons-configuration2.version>2.7</commons-configuration2.version>
<txlcn.version>5.0.2.RELEASE</txlcn.version>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-business</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>code-generator</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-business</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>file-center</artifactId>
<description>文件中心</description>
......
......@@ -56,9 +56,4 @@ public class FileInfo extends Model<FileInfo> {
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-business</artifactId>
<description>业务中心</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-business</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>search-center</artifactId>
<description>搜索中心</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>search-center</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>search-client</artifactId>
<description>搜索中心客户端</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>search-center</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>search-server</artifactId>
<description>搜索中心服务端</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-business</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>user-center</artifactId>
<description>用户中心</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-commons</artifactId>
<description>通用组件</description>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -39,6 +39,7 @@ import java.util.List;
*/
public class CustomRedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String ACCESS_BAK = "access_bak:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
......@@ -118,7 +119,7 @@ public class CustomRedisTokenStore implements TokenStore {
}
private ClientDetails deserializeClientDetails(byte[] bytes) {
return (ClientDetails)redisValueSerializer.deserialize(bytes);
return (ClientDetails) redisValueSerializer.deserialize(bytes);
}
private byte[] serialize(String string) {
......@@ -166,7 +167,7 @@ public class CustomRedisTokenStore implements TokenStore {
//获取过期时长
int validitySeconds = getAccessTokenValiditySeconds(clientAuth.getClientId());
if (validitySeconds > 0) {
double expiresRatio = token.getExpiresIn() / (double)validitySeconds;
double expiresRatio = token.getExpiresIn() / (double) validitySeconds;
//判断是否需要续签,当前剩余时间小于过期时长的50%则续签
if (expiresRatio <= securityProperties.getAuth().getRenew().getTimeRatio()) {
//更新AccessToken过期时间
......@@ -182,6 +183,7 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 判断应用自动续签是否满足白名单和黑名单的过滤逻辑
*
* @param clientId 应用id
* @return 是否满足
*/
......@@ -193,7 +195,7 @@ public class CustomRedisTokenStore implements TokenStore {
List<String> exclusiveClientIds = securityProperties.getAuth().getRenew().getExclusiveClientIds();
if (includeClientIds.size() > 0) {
result = includeClientIds.contains(clientId);
} else if(exclusiveClientIds.size() > 0) {
} else if (exclusiveClientIds.size() > 0) {
result = !exclusiveClientIds.contains(clientId);
}
return result;
......@@ -201,6 +203,7 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 获取token的总有效时长
*
* @param clientId 应用id
*/
private int getAccessTokenValiditySeconds(String clientId) {
......@@ -256,12 +259,14 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 存储token
*
* @param isRenew 是否续签
*/
private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication, boolean isRenew) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] accessBakKey = serializeKey(ACCESS_BAK + token.getValue());
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication));
......@@ -279,6 +284,7 @@ public class CustomRedisTokenStore implements TokenStore {
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, accessBakKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth);
this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken);
} catch (Exception ex) {
......@@ -286,6 +292,7 @@ public class CustomRedisTokenStore implements TokenStore {
}
} else {
conn.set(accessKey, serializedAccessToken);
conn.set(accessBakKey, serializedAccessToken);
conn.set(authKey, serializedAuth);
conn.set(authToAccessKey, serializedAccessToken);
}
......@@ -303,6 +310,7 @@ public class CustomRedisTokenStore implements TokenStore {
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, seconds);
conn.expire(accessBakKey, seconds + 60);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
......@@ -363,6 +371,7 @@ public class CustomRedisTokenStore implements TokenStore {
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] accessBakKey = serializeKey(ACCESS_BAK + tokenValue);
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + tokenValue);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
RedisConnection conn = getConnection();
......@@ -371,6 +380,7 @@ public class CustomRedisTokenStore implements TokenStore {
byte[] auth = conn.get(authKey);
conn.openPipeline();
conn.del(accessKey);
conn.del(accessBakKey);
conn.del(accessToRefreshKey);
// Don't remove the refresh token - it's up to the caller to do that
conn.del(authKey);
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-common-core</artifactId>
<description>公共通用组件</description>
......
......@@ -10,7 +10,7 @@ public interface CommonConstant {
/**
* 项目版本号(banner使用)
*/
String PROJECT_VERSION = "5.2.0";
String PROJECT_VERSION = "5.3.0";
/**
* token请求头名称
......
......@@ -11,4 +11,14 @@ public interface ConfigConstants {
* 是否开启自定义隔离规则
*/
String CONFIG_RIBBON_ISOLATION_ENABLED = "zlt.ribbon.isolation.enabled";
String CONFIG_LOADBALANCE_ISOLATION = "zlt.loadbalance.isolation";
String CONFIG_LOADBALANCE_ISOLATION_ENABLE = CONFIG_LOADBALANCE_ISOLATION + ".enabled";
String CONFIG_LOADBALANCE_ISOLATION_CHOOSER = CONFIG_LOADBALANCE_ISOLATION + ".chooser";
String CONFIG_LOADBALANCE_VERSION = "zlt.loadbalance.version";
}
......@@ -154,6 +154,10 @@ public interface SecurityConstants {
* redis中授权token对应的key
*/
String REDIS_TOKEN_AUTH = "auth:";
/**
* 值同access 过期时间+60
*/
String ACCESS_BAK = "access_bak:";
/**
* redis中应用对应的token集合的key
*/
......
......@@ -27,9 +27,4 @@ public class SuperEntity<T extends Model<?>> extends Model<T> {
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}
......@@ -42,7 +42,7 @@ public class SuperServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M,
) {
if (lock != null) {
//判断记录是否已存在
int count = super.count(countWrapper);
long count = super.count(countWrapper);
if (count == 0) {
return super.save(entity);
} else {
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-common-spring-boot-starter</artifactId>
<description>公共通用组件</description>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
package com.central.db.config;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.central.common.properties.TenantProperties;
import com.central.db.interceptor.CustomTenantInterceptor;
import com.central.db.properties.MybatisPlusAutoFillProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
......@@ -26,10 +26,7 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties(MybatisPlusAutoFillProperties.class)
public class MybatisPlusAutoConfigure {
@Autowired
private TenantHandler tenantHandler;
@Autowired
private ISqlParserFilter sqlParserFilter;
private TenantLineHandler tenantLineHandler;
@Autowired
private TenantProperties tenantProperties;
......@@ -41,17 +38,17 @@ public class MybatisPlusAutoConfigure {
* 分页插件,自动识别数据库类型
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
boolean enableTenant = tenantProperties.getEnable();
//是否开启多租户隔离
if (enableTenant) {
TenantSqlParser tenantSqlParser = new TenantSqlParser()
.setTenantHandler(tenantHandler);
paginationInterceptor.setSqlParserList(CollUtil.toList(tenantSqlParser));
paginationInterceptor.setSqlParserFilter(sqlParserFilter);
CustomTenantInterceptor tenantInterceptor = new CustomTenantInterceptor(
tenantLineHandler, tenantProperties.getIgnoreSqls());
mpInterceptor.addInnerInterceptor(tenantInterceptor);
}
return paginationInterceptor;
return mpInterceptor;
}
@Bean
......
package com.central.db.config;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.central.common.context.TenantContextHolder;
import com.central.common.properties.TenantProperties;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import org.apache.ibatis.mapping.MappedStatement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
......@@ -25,13 +22,13 @@ public class TenantAutoConfigure {
private TenantProperties tenantProperties;
@Bean
public TenantHandler tenantHandler() {
return new TenantHandler() {
public TenantLineHandler tenantLineHandler() {
return new TenantLineHandler() {
/**
* 获取租户id
*/
@Override
public Expression getTenantId(boolean where) {
public Expression getTenantId() {
String tenant = TenantContextHolder.getTenant();
if (tenant != null) {
return new StringValue(TenantContextHolder.getTenant());
......@@ -39,37 +36,16 @@ public class TenantAutoConfigure {
return new NullValue();
}
/**
* 获取租户列名
*/
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
/**
* 过滤不需要根据租户隔离的表
* @param tableName 表名
*/
@Override
public boolean doTableFilter(String tableName) {
public boolean ignoreTable(String tableName) {
return tenantProperties.getIgnoreTables().stream().anyMatch(
(e) -> e.equalsIgnoreCase(tableName)
);
}
};
}
/**
* 过滤不需要根据租户隔离的MappedStatement
*/
@Bean
public ISqlParserFilter sqlParserFilter() {
return metaObject -> {
MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
return tenantProperties.getIgnoreSqls().stream().anyMatch(
(e) -> e.equalsIgnoreCase(ms.getId())
);
};
}
}
package com.central.db.interceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.SQLException;
import java.util.List;
/**
* MyBatis-plus租户拦截器
*
* @author zlt
* @version 1.0
* @date 2022/5/6
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
public class CustomTenantInterceptor extends TenantLineInnerInterceptor {
private List<String> ignoreSqls;
public CustomTenantInterceptor(TenantLineHandler tenantLineHandler, List<String> ignoreSqls) {
super(tenantLineHandler);
this.ignoreSqls = ignoreSqls;
}
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds
, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (isIgnoreMappedStatement(ms.getId())) {
return;
}
super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
private boolean isIgnoreMappedStatement(String msId) {
return ignoreSqls.stream().anyMatch((e) -> e.equalsIgnoreCase(msId));
}
}
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
package com.central.common.lb.chooser;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* service选择器类
*
* @author jarvis create by 2022/3/13
*/
public interface IRuleChooser {
ServiceInstance choose(List<ServiceInstance> instances);
}
package com.central.common.lb.chooser;
import com.alibaba.nacos.common.utils.CollectionUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* 随机的选择器
*
* @author jarvis create by 2022/3/13
*/
@Log4j2
public class RandomRuleChooser implements IRuleChooser {
@Override
public ServiceInstance choose(List<ServiceInstance> instances) {
if(CollectionUtils.isNotEmpty(instances)){
int randomValue = ThreadLocalRandom.current().nextInt(instances.size());
ServiceInstance serviceInstance = instances.get(randomValue);
log.info("选择了ip为{}, 端口为:{}的服务", serviceInstance.getHost(), serviceInstance.getPort());
return serviceInstance;
}
return null;
}
}
package com.central.common.lb.chooser;
import com.alibaba.nacos.common.utils.CollectionUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 轮询选择器
*
* @author jarvis create by 2022/3/13
*/
@Log4j2
public class RoundRuleChooser implements IRuleChooser{
private AtomicInteger position;
public RoundRuleChooser() {
this.position = new AtomicInteger(1000);
}
@Override
public ServiceInstance choose(List<ServiceInstance> instances) {
if(CollectionUtils.isNotEmpty(instances)){
ServiceInstance serviceInstance = instances.get(Math.abs(position.incrementAndGet() % instances.size()));
log.info("选择了ip为{}, 端口为:{}的服务", serviceInstance.getHost(), serviceInstance.getPort());
return serviceInstance;
}
return null;
}
}
......@@ -2,7 +2,10 @@ package com.central.common.lb.config;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.SecurityConstants;
import com.central.common.lb.utils.QueryUtils;
import feign.RequestInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
......@@ -12,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
/**
* feign拦截器,只包含http相关数据
......@@ -37,6 +41,7 @@ public class FeignHttpInterceptorConfig {
* 使用feign client访问别的微服务时,将上游传过来的access_token、username、roles等信息放入header传递给下一个服务
*/
@Bean
@ConditionalOnClass(HttpServletRequest.class)
public RequestInterceptor httpFeignInterceptor() {
return template -> {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
......
package com.central.common.lb.config;
import com.central.common.constant.ConfigConstants;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Import;
/**
*
*
* @author jarvis create by 2022/4/10
*/
@LoadBalancerClients(defaultConfiguration = VersionLoadBalancerConfig.class)
@ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, name = "enabled", havingValue = "true", matchIfMissing = false)
@Import({VersionRegisterBeanPostProcessor.class})
public class VerionIsolationAutoConfig {
}
package com.central.common.lb.config;
import com.central.common.constant.ConfigConstants;
import com.central.common.lb.chooser.IRuleChooser;
import com.central.common.lb.chooser.RoundRuleChooser;
import com.central.common.lb.loadbalancer.VersionLoadBalancer;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;
/**
* 版本控制的路由选择类配置
*
* @author jarvis create by 2022/3/9
*/
@Log4j2
public class VersionLoadBalancerConfig{
private IRuleChooser defaultRuleChooser = null;
@Bean
@ConditionalOnMissingBean(IRuleChooser.class)
@ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, value = "chooser")
public IRuleChooser customRuleChooser(Environment environment, ApplicationContext context){
IRuleChooser chooser = new RoundRuleChooser();
if (environment.containsProperty(ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_CHOOSER)) {
String chooserRuleClassString = environment.getProperty(ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_CHOOSER);
if(StringUtils.isNotBlank(chooserRuleClassString)){
try {
Class<?> ruleClass = ClassUtils.forName(chooserRuleClassString, context.getClassLoader());
chooser = (IRuleChooser) ruleClass.newInstance();
} catch (ClassNotFoundException e) {
log.error("没有找到定义的选择器,将使用内置的选择器", e);
} catch (InstantiationException e) {
log.error("没法创建定义的选择器,将使用内置的选择器", e);
} catch (IllegalAccessException e) {
log.error("没法创建定义的选择器,将使用内置的选择器", e);
}
}
}
return chooser;
}
@Bean
@ConditionalOnMissingBean(value = IRuleChooser.class)
public IRuleChooser defaultRuleChooser(){
return new RoundRuleChooser();
}
@Bean
@ConditionalOnProperty(prefix = ConfigConstants.CONFIG_LOADBALANCE_ISOLATION, name = "enabled", havingValue = "true", matchIfMissing = false)
public ReactorServiceInstanceLoadBalancer versionServiceLoadBalancer(Environment environment
, LoadBalancerClientFactory loadBalancerClientFactory, IRuleChooser ruleChooser){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new VersionLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class)
, name, ruleChooser);
}
}
package com.central.common.lb.config;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.common.utils.StringUtils;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.ConfigConstants;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* 将版本注册到注册中心的组件
*
* @author jarvis create by 2022/3/20
*/
public class VersionRegisterBeanPostProcessor implements BeanPostProcessor {
@Value("${"+ ConfigConstants.CONFIG_LOADBALANCE_VERSION+":}")
private String version;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof NacosDiscoveryProperties && StringUtils.isNotBlank(version)){
NacosDiscoveryProperties nacosDiscoveryProperties = (NacosDiscoveryProperties) bean;
nacosDiscoveryProperties.getMetadata().putIfAbsent(CommonConstant.METADATA_VERSION, version);
}
return bean;
}
}
package com.central.common.lb.loadbalancer;
import com.central.common.constant.CommonConstant;
import com.central.common.lb.chooser.IRuleChooser;
import com.central.common.lb.utils.QueryUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.stream.Collectors;
/**
* 自定义版本路由选择
*
* @author jarvis create by 2022/3/9
*/
@Log4j2
public class VersionLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final static String KEY_DEFAULT = "default";
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSuppliers;
private String serviceId;
private IRuleChooser ruleChooser;
public VersionLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSuppliers, String serviceId, IRuleChooser ruleChooser) {
this.serviceInstanceListSuppliers = serviceInstanceListSuppliers;
this.serviceId = serviceId;
this.ruleChooser = ruleChooser;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 从request中获取版本,兼容webflux方式
RequestData requestData = ((RequestDataContext) (request.getContext())).getClientRequest();
String version = getVersionFromRequestData(requestData);
log.debug("选择的版本号为:{}", version);
return serviceInstanceListSuppliers.getIfAvailable().get(request).next().map(instanceList->{
return getInstanceResponse(instanceList, version);
});
}
private String getVersionFromRequestData(RequestData requestData){
Map<String, String> queryMap = QueryUtils.getQueryMap(requestData.getUrl());
if(MapUtils.isNotEmpty(queryMap)&& queryMap.containsKey(CommonConstant.Z_L_T_VERSION)&& StringUtils.isNotBlank(queryMap.get(CommonConstant.Z_L_T_VERSION))){
return queryMap.get(CommonConstant.Z_L_T_VERSION);
}else if(requestData.getHeaders().containsKey(CommonConstant.Z_L_T_VERSION)){
return requestData.getHeaders().get(CommonConstant.Z_L_T_VERSION).get(0);
}
return null;
}
/**
* 1. 先获取到拦截的版本,如果不为空的话就将service列表过滤,寻找metadata中哪个服务是配置的版本,
* 如果版本为空则不需要进行过滤直接提交给service选择器进行选择
* 2. 如果没有找到版本对应的实例,则找所有版本为空或者版本号为default的实例
* 3.将instance列表提交到选择器根据对应的策略返回一个instance
* @param instances
* @return
*/
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance>instances, String version){
List<ServiceInstance> filteredServiceIstanceList = instances;
if(StringUtils.isNotBlank(version)){
if(CollectionUtils.isNotEmpty(instances)){
filteredServiceIstanceList = instances.stream()
.filter(item->item.getMetadata().containsKey(CommonConstant.METADATA_VERSION)&&
version.equals(item.getMetadata().get(CommonConstant.METADATA_VERSION)))
.collect(Collectors.toList());
}
}
// 如果没有找到对应的版本实例时,选择版本号为空的或这版本为default的实例
if(CollectionUtils.isEmpty(filteredServiceIstanceList)){
filteredServiceIstanceList = instances.stream()
.filter(item->!item.getMetadata().containsKey(CommonConstant.METADATA_VERSION)||
StringUtils.isBlank(item.getMetadata().get(CommonConstant.METADATA_VERSION))
|| "default".equals(item.getMetadata().get(CommonConstant.METADATA_VERSION)))
.collect(Collectors.toList());
}
// 经过两轮过滤后如果能找到的话就选择,不然返回空
if(CollectionUtils.isNotEmpty(filteredServiceIstanceList)){
ServiceInstance serviceInstance = this.ruleChooser.choose(filteredServiceIstanceList);
if(!Objects.isNull(serviceInstance)){
log.debug("使用serviceId为:{}服务, 选择version为:{}, 地址:{}:{},", serviceId, version
, serviceInstance.getHost(), serviceInstance.getPort());
return new DefaultResponse(serviceInstance);
}
}
// 返回空的返回体
return new EmptyResponse();
}
}
package com.central.common.lb.utils;
import org.apache.commons.lang3.StringUtils;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 解析request的query参数工具
*
* @author jarvis create by 2022/5/8
*/
public class QueryUtils {
/**
* 通过query字符串得到参数的map
* @param queryString ?后的字符
* @return
*/
public static Map<String, String> getQueryMap(String queryString){
if(StringUtils.isNotBlank(queryString)){
return Arrays.stream(queryString.split("&")).map(item -> item.split("="))
.collect(Collectors.toMap(key -> key[0], value -> value.length > 1 && StringUtils.isNotBlank(value[1]) ? value[1] : ""));
}
return Collections.emptyMap();
}
/**
* 通过url获取参数map
* @param uri
* @return
*/
public static Map<String, String> getQueryMap(URI uri){
if(Objects.nonNull(uri)){
return getQueryMap(uri.getQuery());
}
return Collections.emptyMap();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.central.common.lb.RestTemplateAutoConfigure
\ No newline at end of file
com.central.common.lb.RestTemplateAutoConfigure,\
com.central.common.lb.config.VerionIsolationAutoConfig
\ No newline at end of file
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
......
......@@ -34,4 +34,7 @@ zlt.trace.enable=true
zlt.ribbon.isolation.enabled=false
##### mybatis-plus打印完整sql(只适用于开发环境)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
\ No newline at end of file
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启版本路由
#zlt.loadbalance.isolation.enabled=true
\ No newline at end of file
......@@ -6,7 +6,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>dubbo-demo</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-demo</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>rocketmq-demo</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>rocketmq-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>rocketmq-consume</artifactId>
<description>rocketMQ消费者demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>rocketmq-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>rocketmq-produce</artifactId>
<description>rocketMQ生产者demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>rocketmq-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>rocketmq-transactional</artifactId>
<description>rocketMQ事务消息demo</description>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>seata-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>account-service</artifactId>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>seata-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>business-service</artifactId>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>seata-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>order-service</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>seata-demo</artifactId>
<description>seata分布式事务demo</description>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>seata-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>seata-common-starter</artifactId>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>seata-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>storage-service</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>sharding-jdbc-demo</artifactId>
<description>sharding-jdbc分库分表demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>sso-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>oidc-sso</artifactId>
<description>OIDC协议单点登录demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>sso-demo</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>sso-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>ss-sso</artifactId>
<description>springSecurity单点登录demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>sso-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>web-sso</artifactId>
<description>前后端分离单点登录demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>txlcn-demo</artifactId>
<description>txlcn分布式事务demo</description>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>txlcn-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>txlcn-demo-common</artifactId>
<name>demo-common</name>
......
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>txlcn-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -16,4 +16,10 @@ tx-lcn:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://${zlt.datasource.ip}:3306/tx_logger?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
username: ${zlt.datasource.username}
password: ${zlt.datasource.password}
\ No newline at end of file
password: ${zlt.datasource.password}
zlt:
loadbalance:
version: test
isolation:
enabled: true
chooser: com.central.common.lb.chooser.RandomRuleChooser
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>txlcn-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>txlcn-demo-spring-service-b</artifactId>
......
......@@ -16,4 +16,9 @@ tx-lcn:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://${zlt.datasource.ip}:3306/tx_logger?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
username: ${zlt.datasource.username}
password: ${zlt.datasource.password}
\ No newline at end of file
password: ${zlt.datasource.password}
zlt:
loadbalance:
version: test
isolation:
enabled: true
\ No newline at end of file
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>txlcn-demo</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>txlcn-demo-spring-service-c</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-gateway</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-gateway</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>sc-gateway</artifactId>
<description>spring cloud gateway网关</description>
......
package com.central.gateway.filter;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.ConfigConstants;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* 示例
*
* @author jarvis create by 2022/5/8
*/
@Component
public class VersionLbIsolationFilter implements GlobalFilter, Ordered {
@Value("${"+ ConfigConstants.CONFIG_LOADBALANCE_ISOLATION_ENABLE+":}")
private Boolean enableVersionControl;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if(Boolean.TRUE.equals(enableVersionControl)
&& exchange.getRequest().getQueryParams().containsKey(CommonConstant.Z_L_T_VERSION)){
String version = exchange.getRequest().getQueryParams().get(CommonConstant.Z_L_T_VERSION).get(0);
ServerHttpRequest rebuildRequest = exchange.getRequest().mutate().headers(header -> {
header.add(CommonConstant.Z_L_T_VERSION, version);
}).build();
ServerWebExchange rebuildServerWebExchange = exchange.mutate().request(rebuildRequest).build();
return chain.filter(rebuildServerWebExchange);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-job</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>job-admin</artifactId>
<packaging>jar</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-job</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>job-core</artifactId>
<packaging>jar</packaging>
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-job</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>job-executor-samples</artifactId>
<packaging>jar</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-job</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-monitor</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>log-center</artifactId>
<dependencies>
......
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-monitor</artifactId>
<packaging>pom</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-monitor</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>sc-admin</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-transaction</artifactId>
<description>事务</description>
......
......@@ -3,7 +3,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-transaction</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>txlcn-tm</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-uaa</artifactId>
<description>认证中心</description>
......
package com.central.oauth.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
*
* redis过期key监听器配置类
* @author zlt
*
*/
@Configuration
public class RedisListenerConfig {
@Bean
@Primary
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
return container;
}
}
package com.central.oauth.listener;
import com.central.common.constant.SecurityConstants;
import com.central.common.redis.template.RedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.stereotype.Component;
/**
*
* redis过期key监听器
* @author zlt
*
*/
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
@Autowired
private RedisRepository redisRepository;
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private final RedisConnectionFactory connectionFactory;
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer, RedisConnectionFactory connectionFactory) {
super(listenerContainer);
this.connectionFactory = connectionFactory;
}
@Override
public void onMessage(Message message, byte[] pattern) {
if (message == null) {
log.debug("message不能为空");
return;
}
//获取失效的的key
String expiredKey = message.toString();
if (StringUtils.isEmpty(expiredKey)) {
log.debug("expiredKey不能为空");
return;
}
String accesskey = expiredKey.substring(0, expiredKey.indexOf(":") + 1);
if (!"access:".equals(accesskey)) {
log.debug("非需要监听key,跳过");
return;
}
String accessValue = expiredKey.substring(expiredKey.indexOf(":") + 1);
// 分布式集群部署下防止一个过期被多个服务重复消费
String qc = "qc:" + accessValue;
String oldLock = redisRepository.getAndSet(qc, "1");
if (StringUtils.isNotEmpty(oldLock) && "1".equals(oldLock)) {
log.debug("其他节点已经处理了该数据,跳过");
return;
}
byte[] accessBakKey = serializeKey(SecurityConstants.ACCESS_BAK + accessValue);
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + accessValue);
RedisConnection conn = getConnection();
try {
byte[] access = conn.get(accessBakKey);
byte[] auth = conn.get(authKey);
OAuth2Authentication authentication = deserializeAuthentication(auth);
if (authentication != null) {
byte[] unameKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
conn.openPipeline();
conn.lRem(unameKey, 1, access);
conn.lRem(clientId, 1, access);
conn.closePipeline();
}
} catch (Exception e) {
log.error(e.getMessage());
} finally {
conn.del(serializeKey(qc));
conn.close();
}
}
private byte[] serializeKey(String object) {
return serialize("" + object);
}
private byte[] serialize(String string) {
return serializationStrategy.serialize(string);
}
private RedisConnection getConnection() {
return connectionFactory.getConnection();
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? ""
: authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
}
......@@ -6,7 +6,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-web</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>back-web</artifactId>
<description>后台管理前端</description>
......
......@@ -6,7 +6,7 @@
<parent>
<groupId>com.zlt</groupId>
<artifactId>central-platform</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</parent>
<artifactId>zlt-web</artifactId>
<description>前端</description>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册