Merge branch 'refs/heads/zt-test' into test
# Conflicts: # zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/biz/system/permission/PermissionCommonApi.java # zt-framework/zt-spring-boot-starter-biz-tenant/src/main/java/com/zt/plat/framework/tenant/core/context/DeptContextHolder.java # zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/permission/PermissionServiceImpl.java
This commit is contained in:
@@ -192,24 +192,4 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.zt.plat.module.databus;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Databus 模块的启动类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class DatabusServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DatabusServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,9 @@ public class ApiClientCredentialRespVO {
|
||||
@Schema(description = "匿名访问固定用户昵称", example = "张三")
|
||||
private String anonymousUserNickname;
|
||||
|
||||
@Schema(description = "是否启用加密", example = "true")
|
||||
private Boolean enableEncryption;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
@@ -45,4 +45,8 @@ public class ApiClientCredentialSaveReqVO {
|
||||
@Schema(description = "匿名访问固定用户 ID", example = "1024")
|
||||
private Long anonymousUserId;
|
||||
|
||||
@Schema(description = "是否启用加密", example = "true")
|
||||
@NotNull(message = "启用加密标识不能为空")
|
||||
private Boolean enableEncryption;
|
||||
|
||||
}
|
||||
|
||||
@@ -38,4 +38,6 @@ public class ApiClientCredentialDO extends BaseDO {
|
||||
|
||||
private Long anonymousUserId;
|
||||
|
||||
private Boolean enableEncryption;
|
||||
|
||||
}
|
||||
|
||||
@@ -108,7 +108,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
||||
credential = credentialService.findActiveCredential(appId)
|
||||
.orElseThrow(() -> new SecurityValidationException(HttpStatus.UNAUTHORIZED, "应用凭证不存在或已禁用"));
|
||||
boolean allowAnonymous = Boolean.TRUE.equals(credential.getAllowAnonymous());
|
||||
boolean enableEncryption = Boolean.TRUE.equals(credential.getEnableEncryption());
|
||||
ApiAnonymousUserService.AnonymousUserDetails anonymousDetails = null;
|
||||
byte[] requestBody = StreamUtils.copyToByteArray(request.getInputStream());
|
||||
if (allowAnonymous) {
|
||||
Long anonymousUserId = credential.getAnonymousUserId();
|
||||
if (anonymousUserId == null) {
|
||||
@@ -117,24 +119,25 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
||||
anonymousDetails = anonymousUserService.find(anonymousUserId)
|
||||
.orElseThrow(() -> new SecurityValidationException(HttpStatus.UNAUTHORIZED, "匿名访问固定用户不可用"));
|
||||
}
|
||||
|
||||
String timestampHeader = requireHeader(request, TIMESTAMP_HEADER, "缺少时间戳");
|
||||
// 校验时间戳与随机数,防止请求被重放
|
||||
validateTimestamp(timestampHeader, security);
|
||||
String nonce = requireHeader(request, NONCE_HEADER, "缺少随机数");
|
||||
if (nonce.length() < 8) {
|
||||
throw new SecurityValidationException(HttpStatus.BAD_REQUEST, "随机数长度不足");
|
||||
}
|
||||
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
|
||||
if (enableEncryption){
|
||||
String nonce = requireHeader(request, NONCE_HEADER, "缺少随机数");
|
||||
if (nonce.length() < 8) {
|
||||
throw new SecurityValidationException(HttpStatus.BAD_REQUEST, "随机数长度不足");
|
||||
}
|
||||
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
|
||||
|
||||
byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream());
|
||||
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
|
||||
byte[] decryptedBody = decryptRequestBody(originalBody, credential, security);
|
||||
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
|
||||
ensureNonce(tenantId, appId, nonce, security);
|
||||
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
|
||||
byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
|
||||
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
|
||||
ensureNonce(tenantId, appId, nonce, security);
|
||||
requestBody = decryptedBody;
|
||||
}
|
||||
|
||||
// 使用可重复读取的请求包装,供后续过滤器继续消费
|
||||
CachedBodyHttpServletRequest securedRequest = new CachedBodyHttpServletRequest(request, decryptedBody);
|
||||
CachedBodyHttpServletRequest securedRequest = new CachedBodyHttpServletRequest(request, requestBody);
|
||||
securedRequest.setHeader(APP_ID_HEADER, credential.getAppId());
|
||||
securedRequest.setHeader(HEADER_CREDENTIAL_ID, credential.getId() != null ? String.valueOf(credential.getId()) : null);
|
||||
ApiGatewayAccessLogger.propagateLogIdHeader(securedRequest, accessLogId);
|
||||
@@ -238,6 +241,11 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
||||
private byte[] decryptRequestBody(byte[] originalBody,
|
||||
ApiClientCredentialDO credential,
|
||||
ApiGatewayProperties.Security security) {
|
||||
// 检查是否启用加密,如果未启用则直接返回原文
|
||||
if (credential != null && Boolean.FALSE.equals(credential.getEnableEncryption())) {
|
||||
return originalBody != null ? originalBody : new byte[0];
|
||||
}
|
||||
|
||||
if (originalBody == null || originalBody.length == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
@@ -390,6 +398,11 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
||||
private void encryptResponse(ContentCachingResponseWrapper responseWrapper,
|
||||
ApiClientCredentialDO credential,
|
||||
ApiGatewayProperties.Security security) throws IOException {
|
||||
// 检查是否启用加密,如果未启用则直接返回,不加密响应
|
||||
if (credential != null && Boolean.FALSE.equals(credential.getEnableEncryption())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!security.isEncryptResponse()) {
|
||||
return;
|
||||
}
|
||||
@@ -524,6 +537,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
||||
if (security == null || credential == null) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否启用加密,如果未启用则不加密错误响应
|
||||
if (Boolean.FALSE.equals(credential.getEnableEncryption())) {
|
||||
return false;
|
||||
}
|
||||
if (!security.isEncryptResponse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ public class HttpStepHandler implements ApiStepHandler {
|
||||
private final WebClient.Builder webClientBuilder;
|
||||
private final ExpressionExecutor expressionExecutor;
|
||||
|
||||
private static final Duration RETRY_DELAY = Duration.ofMillis(200);
|
||||
private static final int RETRY_ATTEMPTS = 3;
|
||||
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(20);
|
||||
private static final Duration RETRY_DELAY = Duration.ofSeconds(5);
|
||||
private static final int RETRY_ATTEMPTS = 5;
|
||||
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(40);
|
||||
|
||||
private static final Set<String> DEFAULT_FORWARDED_HEADERS = Set.of(
|
||||
"authorization",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.zt.plat.module.databus.service.gateway.impl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
@@ -11,14 +9,12 @@ import com.zt.plat.module.databus.dal.dataobject.gateway.ApiClientCredentialDO;
|
||||
import com.zt.plat.module.databus.dal.mysql.gateway.ApiClientCredentialMapper;
|
||||
import com.zt.plat.module.databus.service.gateway.ApiAnonymousUserService;
|
||||
import com.zt.plat.module.databus.service.gateway.ApiClientCredentialService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -36,16 +32,6 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
private final ApiClientCredentialMapper credentialMapper;
|
||||
private final ApiAnonymousUserService anonymousUserService;
|
||||
|
||||
private LoadingCache<String, Optional<ApiClientCredentialDO>> credentialCache;
|
||||
|
||||
@PostConstruct
|
||||
public void initCache() {
|
||||
credentialCache = Caffeine.newBuilder()
|
||||
.maximumSize(256)
|
||||
.expireAfterWrite(Duration.ofMinutes(5))
|
||||
.build(this::loadCredentialSync);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ApiClientCredentialDO> getPage(ApiClientCredentialPageReqVO reqVO) {
|
||||
return credentialMapper.selectPage(reqVO);
|
||||
@@ -67,7 +53,6 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
credential.setAnonymousUserId(null);
|
||||
}
|
||||
credentialMapper.insert(credential);
|
||||
invalidateCache(credential.getAppId());
|
||||
return credential.getId();
|
||||
}
|
||||
|
||||
@@ -86,8 +71,6 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
updateObj.setAnonymousUserId(null);
|
||||
}
|
||||
credentialMapper.updateById(updateObj);
|
||||
invalidateCache(existing.getAppId());
|
||||
invalidateCache(updateObj.getAppId());
|
||||
if (!Objects.equals(existing.getAnonymousUserId(), updateObj.getAnonymousUserId())) {
|
||||
anonymousUserService.invalidate(existing.getAnonymousUserId());
|
||||
anonymousUserService.invalidate(updateObj.getAnonymousUserId());
|
||||
@@ -99,7 +82,6 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
public void delete(Long id) {
|
||||
ApiClientCredentialDO existing = ensureExists(id);
|
||||
credentialMapper.deleteById(id);
|
||||
invalidateCache(existing.getAppId());
|
||||
anonymousUserService.invalidate(existing.getAnonymousUserId());
|
||||
}
|
||||
|
||||
@@ -118,11 +100,7 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
if (!StringUtils.hasText(appId)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return credentialCache.get(appId.trim());
|
||||
}
|
||||
|
||||
private Optional<ApiClientCredentialDO> loadCredentialSync(String appId) {
|
||||
Optional<ApiClientCredentialDO> credential = credentialMapper.selectByAppId(appId)
|
||||
Optional<ApiClientCredentialDO> credential = credentialMapper.selectByAppId(appId.trim())
|
||||
.filter(item -> Boolean.TRUE.equals(item.getEnabled()));
|
||||
if (credential.isEmpty()) {
|
||||
log.debug("[API-PORTAL] 未找到 appId={} 的有效凭证", appId);
|
||||
@@ -147,13 +125,6 @@ public class ApiClientCredentialServiceImpl implements ApiClientCredentialServic
|
||||
return credential;
|
||||
}
|
||||
|
||||
private void invalidateCache(String appId) {
|
||||
if (!StringUtils.hasText(appId)) {
|
||||
return;
|
||||
}
|
||||
credentialCache.invalidate(appId.trim());
|
||||
}
|
||||
|
||||
private void normalizeAnonymousSettings(ApiClientCredentialSaveReqVO reqVO) {
|
||||
if (Boolean.TRUE.equals(reqVO.getAllowAnonymous())) {
|
||||
if (reqVO.getAnonymousUserId() == null) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user