From a0ec76a8d8d0928f29c74f5688594a8361e46c30 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 15 Jan 2026 17:50:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?iwork=20=E5=90=8C=E6=AD=A5=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=9A=84=E8=A7=84=E5=88=99=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iwork/impl/IWorkSyncProcessorImpl.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java index 0d44b790..3f7aeda0 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java @@ -326,9 +326,8 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { boolean inactive = isInactiveUser(user.getStatus()); String username = resolveUsername(user); if (StrUtil.isBlank(username)) { - log.warn("[iWork] 人员缺少可用账号(工号={}, 登录账号={}),跳过:id={} name={}", - user.getWorkcode(), user.getLoginid(), user.getId(), user.getLastname()); - result.increaseFailed(); + logSkip("人员", user.getId(), "缺少工号与登录账号,跳过同步"); + result.increaseSkipped(); continue; } try { @@ -545,7 +544,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { CommonStatusEnum status) { UserSaveReqVO req = new UserSaveReqVO(); req.setUsername(username); - req.setWorkcode(trimToNull(source.getWorkcode())); + req.setWorkcode(resolveWorkcode(source)); req.setNickname(limitLength(StrUtil.blankToDefault(source.getLastname(), username), 30)); req.setRemark(buildUserRemark(source)); if (deptId != null) { @@ -776,6 +775,26 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { * 工号优先、登录账号兜底,确保账号体系与 iWork 一致 */ private String resolveUsername(IWorkHrUserPageRespVO.User user) { + if (user == null) { + return null; + } + String workcode = resolveWorkcode(user); + if (StrUtil.isNotBlank(workcode)) { + return workcode; + } + if (StrUtil.isNotBlank(user.getLoginid())) { + return user.getLoginid().trim(); + } + return null; + } + + /** + * 工号为空时自动使用登录账号作为工号兜底,避免因缺失工号而跳过同步。 + */ + private String resolveWorkcode(IWorkHrUserPageRespVO.User user) { + if (user == null) { + return null; + } if (StrUtil.isNotBlank(user.getWorkcode())) { return user.getWorkcode().trim(); } From 2a1af3ed6b2382d48cb66313e8e3c4e1a4d06d5b Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 15 Jan 2026 18:04:11 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=90=8C=E6=AD=A5=20feign=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=9F=A5=E8=AF=A2=E7=9A=84=E7=94=A8=E6=88=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plat/module/system/api/user/dto/AdminUserRespDTO.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/user/dto/AdminUserRespDTO.java b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/user/dto/AdminUserRespDTO.java index 60fada58..57bec45e 100644 --- a/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/user/dto/AdminUserRespDTO.java +++ b/zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/user/dto/AdminUserRespDTO.java @@ -4,6 +4,7 @@ import com.fhs.core.trans.vo.VO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.time.LocalDateTime; import java.util.List; import java.util.Set; @@ -20,6 +21,12 @@ public class AdminUserRespDTO implements VO { @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王") private String nickname; + @Schema(description = "工号", example = "A00123") + private String workcode; + + @Schema(description = "备注", example = "我是一个用户") + private String remark; + @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long tenantId; From 06fb92462c4ca79029b4a6a7c0abc00c3516c0d2 Mon Sep 17 00:00:00 2001 From: wuzongyong <13203449218@163.com> Date: Thu, 15 Jan 2026 18:06:26 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(config):=20=E6=9B=B4=E6=96=B0=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E7=8E=AF=E5=A2=83=E6=95=B0=E6=8D=AE=E5=BA=93=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=92=8C=E5=AE=89=E5=85=A8=E8=BF=87=E6=BB=A4=E5=99=A8?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 application-dev.yml 中的数据库连接地址和凭据信息 - 在 GatewaySecurityFilter 中实现条件加密验证逻辑 - 添加 wzy 环境配置文件支持 Nacos 配置中心连接 - 优化请求体解密和签名验证流程以支持选择性加密处理 - 更新缓存请求体构造以确保解密后数据正确传递 --- pom.xml | 13 +++++++++ .../src/main/resources/application-dev.yml | 8 +++--- .../security/GatewaySecurityFilter.java | 27 ++++++++++--------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 6743a5ca..1b493aeb 100644 --- a/pom.xml +++ b/pom.xml @@ -243,6 +243,19 @@ 1.0.0 + + wzy + + dev + + 172.16.46.63:30848 + wzy + DEFAULT_GROUP + nacos + P@ssword25 + 1.0.0 + + klw-dev diff --git a/zt-module-databus/zt-module-databus-server-app/src/main/resources/application-dev.yml b/zt-module-databus/zt-module-databus-server-app/src/main/resources/application-dev.yml index 87dad43b..0428df40 100644 --- a/zt-module-databus/zt-module-databus-server-app/src/main/resources/application-dev.yml +++ b/zt-module-databus/zt-module-databus-server-app/src/main/resources/application-dev.yml @@ -37,14 +37,14 @@ spring: primary: master datasource: master: - url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + url: jdbc:dm://172.17.11.98:20870?schema=JYGK_TEST username: SYSDBA - password: pgbsci6ddJ6Sqj@e + password: P@ssword25 slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO + url: jdbc:dm://172.17.11.98:20870?schema=JYGK_TEST username: SYSDBA - password: pgbsci6ddJ6Sqj@e + password: P@ssword25 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java index b37dfb76..7bd4e98f 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java @@ -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); From d6e72f60457914b09bea7335f9baf26a78092607 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 15 Jan 2026 21:25:16 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AF=86=E7=A0=81=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E7=9A=84=E7=94=A8=E6=88=B7=E4=B9=9F=E8=BF=9B=E8=A1=8C=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integration/iwork/impl/IWorkSyncProcessorImpl.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java index 3f7aeda0..5547d727 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java @@ -28,7 +28,10 @@ import com.zt.plat.module.system.util.sync.SyncVerifyUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.util.DigestUtils; +import java.nio.charset.StandardCharsets; +import java.util.Locale; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -39,6 +42,8 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { private static final String JOB_CODE_PREFIX = "IWORK_JOB_"; private static final int DEFAULT_SORT = 999; + /** 当上游密码缺失时,用空字符串的 MD5 作为占位,保证账号可创建 */ + private static final String EMPTY_PASSWORD_PLACEHOLDER = DigestUtils.md5DigestAsHex("".getBytes(StandardCharsets.UTF_8)).toUpperCase(Locale.ROOT); private final DeptService deptService; private final PostService postService; @@ -349,9 +354,8 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { continue; } if (StrUtil.isBlank(externalPassword)) { - log.warn("[iWork] 人员缺少密码信息,无法创建:id={} username={}", user.getId(), username); - result.increaseFailed(); - continue; + externalPassword = EMPTY_PASSWORD_PLACEHOLDER; + log.info("[iWork] 人员缺少密码信息,使用空密码占位同步:id={} username={}", user.getId(), username); } outcome = createUser(user, username, deptId, postId, status, externalPassword); } else {