From ce39dc6d4bd6c654648325f742b78df793539457 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Fri, 21 Nov 2025 18:20:11 +0800 Subject: [PATCH] =?UTF-8?q?1.=20iwork=20=E4=BA=8C=E6=AC=A1=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iwork/IWorkIntegrationController.java | 16 ++ .../iwork/vo/IWorkAuthRegisterReqVO.java | 14 ++ .../iwork/vo/IWorkAuthRegisterRespVO.java | 26 +++ .../iwork/vo/IWorkAuthTokenReqVO.java | 16 ++ .../iwork/vo/IWorkAuthTokenRespVO.java | 29 ++++ .../iwork/config/IWorkProperties.java | 6 +- .../IWorkIntegrationErrorCodeConstants.java | 2 +- .../iwork/IWorkIntegrationService.java | 14 ++ .../impl/IWorkIntegrationServiceImpl.java | 163 +++++++++++++----- .../src/main/resources/application.yaml | 9 +- 10 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterReqVO.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterRespVO.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenReqVO.java create mode 100644 zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenRespVO.java diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java index f0d679ad..f66a0f2a 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java @@ -1,6 +1,10 @@ package com.zt.plat.module.system.controller.admin.integration.iwork; import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterRespVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenRespVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO; @@ -39,6 +43,18 @@ public class IWorkIntegrationController { private final IWorkIntegrationService integrationService; private final IWorkOrgRestService orgRestService; + @PostMapping("/auth/register") + @Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret") + public CommonResult register(@Valid @RequestBody IWorkAuthRegisterReqVO reqVO) { + return success(integrationService.registerSession(reqVO)); + } + + @PostMapping("/auth/token") + @Operation(summary = "申请 iWork Token(独立接口)") + public CommonResult acquireToken(@Valid @RequestBody IWorkAuthTokenReqVO reqVO) { + return success(integrationService.acquireToken(reqVO)); + } + @PostMapping("/user/resolve") @Operation(summary = "根据外部标识获取 iWork 用户编号") public CommonResult resolveUser(@Valid @RequestBody IWorkUserInfoReqVO reqVO) { diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterReqVO.java new file mode 100644 index 00000000..58eecaa8 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.system.controller.admin.integration.iwork.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 请求重新向 iWork 注册以换取服务端公钥与 secret。 + */ +@Data +public class IWorkAuthRegisterReqVO { + + @Schema(description = "是否强制刷新注册信息", example = "false") + private Boolean forceRefreshRegistration; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterRespVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterRespVO.java new file mode 100644 index 00000000..41f4aae1 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthRegisterRespVO.java @@ -0,0 +1,26 @@ +package com.zt.plat.module.system.controller.admin.integration.iwork.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 返回 iWork 注册后的公钥与密钥信息。 + */ +@Data +public class IWorkAuthRegisterRespVO { + + @Schema(description = "使用的 iWork 应用编号", example = "iwork-app") + private String appId; + + @Schema(description = "本地配置的客户端公钥(Base64)") + private String clientPublicKey; + + @Schema(description = "自动生成的客户端私钥(Base64),仅在未配置固定公钥时返回") + private String clientPrivateKey; + + @Schema(description = "iWork 返回的 server public key(Base64)") + private String serverPublicKey; + + @Schema(description = "iWork 返回的 secret,用于后续申请 token") + private String secret; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenReqVO.java new file mode 100644 index 00000000..9992e869 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenReqVO.java @@ -0,0 +1,16 @@ +package com.zt.plat.module.system.controller.admin.integration.iwork.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 申请 iWork token 的请求参数。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IWorkAuthTokenReqVO extends IWorkBaseReqVO { + + @Schema(description = "是否强制重新执行注册流程") + private Boolean forceRefreshRegistration; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenRespVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenRespVO.java new file mode 100644 index 00000000..1c490044 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkAuthTokenRespVO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.system.controller.admin.integration.iwork.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 申请 token 的返回结果。 + */ +@Data +public class IWorkAuthTokenRespVO { + + @Schema(description = "使用的 iWork appId", example = "iwork-app") + private String appId; + + @Schema(description = "作为操作人的 iWork 用户编号", example = "1") + private String operatorUserId; + + @Schema(description = "iWork 返回的访问 token") + private String token; + + @Schema(description = "与 token 匹配的加密 userId,供 header 直接使用") + private String encryptedUserId; + + @Schema(description = "token 预计过期时间(Epoch 秒)") + private Long expiresAtEpochSecond; + + @Schema(description = "当前会话对应的 server public key") + private String serverPublicKey; +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/integration/iwork/config/IWorkProperties.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/integration/iwork/config/IWorkProperties.java index 63e514b2..92fc5b6b 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/integration/iwork/config/IWorkProperties.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/integration/iwork/config/IWorkProperties.java @@ -85,12 +85,12 @@ public class IWorkProperties { @Getter public static class Headers { - private final String appId = "app-id"; - private final String clientPublicKey = "client-public-key"; + private final String appId = "appid"; + private final String clientPublicKey = "cpk"; private final String secret = "secret"; private final String token = "token"; private final String time = "time"; - private final String userId = "user-id"; + private final String userId = "userid"; } @Data diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationErrorCodeConstants.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationErrorCodeConstants.java index 0a0a8324..fc9df67c 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationErrorCodeConstants.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationErrorCodeConstants.java @@ -9,7 +9,7 @@ public interface IWorkIntegrationErrorCodeConstants { ErrorCode IWORK_BASE_URL_MISSING = new ErrorCode(1_010_200_001, "iWork 集成未配置网关地址"); ErrorCode IWORK_CONFIGURATION_INVALID = new ErrorCode(1_010_200_002, - "iWork 集成缺少必填配置(appId/clientPublicKey/userId/workflowId)"); + "iWork 集成缺少必填配置(appId/userId/workflowId)或配置无效"); ErrorCode IWORK_REGISTER_FAILED = new ErrorCode(1_010_200_003, "iWork 注册授权失败"); ErrorCode IWORK_APPLY_TOKEN_FAILED = new ErrorCode(1_010_200_004, "iWork 令牌申请失败"); ErrorCode IWORK_REMOTE_REQUEST_FAILED = new ErrorCode(1_010_200_005, "iWork 接口请求失败"); diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationService.java index c268029f..9d3ffc70 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationService.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkIntegrationService.java @@ -1,5 +1,9 @@ package com.zt.plat.module.system.service.integration.iwork; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterRespVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenRespVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoReqVO; import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoRespVO; @@ -11,6 +15,16 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWork */ public interface IWorkIntegrationService { + /** + * 主动触发注册流程,获取 iWork 返回的服务端公钥与 secret。 + */ + IWorkAuthRegisterRespVO registerSession(IWorkAuthRegisterReqVO reqVO); + + /** + * 主动向 iWork 申请访问 token,并返回相关会话信息。 + */ + IWorkAuthTokenRespVO acquireToken(IWorkAuthTokenReqVO reqVO); + /** * 根据外部标识解析 iWork 内部用户编号。 */ diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java index f0d944f2..f2084ebd 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java @@ -9,27 +9,14 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.zt.plat.framework.common.exception.ErrorCode; import com.zt.plat.framework.common.exception.ServiceException; import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDetailRecordVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDetailTableVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFormFieldVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoReqVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoRespVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCreateReqVO; -import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowVoidReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*; import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties; import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; import lombok.extern.slf4j.Slf4j; -import okhttp3.FormBody; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; +import okhttp3.*; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; @@ -39,16 +26,10 @@ import org.springframework.util.StringUtils; import javax.crypto.Cipher; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.security.KeyFactory; -import java.security.PublicKey; +import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.time.Instant; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.*; @@ -71,21 +52,68 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { .maximumSize(256) .build(); - private final Cache publicKeyCache = Caffeine.newBuilder() + private final Cache registrationCache = Caffeine.newBuilder() + .maximumSize(32) + .build(); + + private final Cache clientKeyCache = Caffeine.newBuilder() + .maximumSize(32) + .build(); + + private final Cache publicKeyCache = Caffeine.newBuilder() .maximumSize(64) .build(); - private volatile OkHttpClient cachedHttpClient; + private volatile OkHttpClient cachedHttpClient; + + @Override + public IWorkAuthRegisterRespVO registerSession(IWorkAuthRegisterReqVO reqVO) { + assertConfigured(); + String appId = resolveAppId(); + boolean forceRefresh = reqVO != null && Boolean.TRUE.equals(reqVO.getForceRefreshRegistration()); + ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, forceRefresh); + RegistrationState registration = obtainRegistration(appId, clientKeyPair, forceRefresh); + + IWorkAuthRegisterRespVO respVO = new IWorkAuthRegisterRespVO(); + respVO.setAppId(appId); + respVO.setClientPublicKey(clientKeyPair.publicKey()); + respVO.setClientPrivateKey(clientKeyPair.privateKey()); + respVO.setServerPublicKey(registration.spk()); + respVO.setSecret(registration.secret()); + return respVO; + } + + @Override + public IWorkAuthTokenRespVO acquireToken(IWorkAuthTokenReqVO reqVO) { + assertConfigured(); + String appId = resolveAppId(); + String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId()); + boolean forceRegistration = Boolean.TRUE.equals(reqVO.getForceRefreshRegistration()); + boolean forceToken = Boolean.TRUE.equals(reqVO.getForceRefreshToken()); + boolean force = forceRegistration || forceToken; + + ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, forceRegistration); + IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, force); + + IWorkAuthTokenRespVO respVO = new IWorkAuthTokenRespVO(); + respVO.setAppId(appId); + respVO.setOperatorUserId(operatorUserId); + respVO.setToken(session.getToken()); + respVO.setEncryptedUserId(session.getEncryptedUserId()); + respVO.setExpiresAtEpochSecond(session.getExpiresAt().getEpochSecond()); + respVO.setServerPublicKey(session.getSpk()); + return respVO; + } @Override public IWorkUserInfoRespVO resolveUserId(IWorkUserInfoReqVO reqVO) { assertConfigured(); String appId = resolveAppId(); - String clientPublicKey = resolveClientPublicKey(); + ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId()); ensureIdentifier(reqVO.getIdentifierKey(), reqVO.getIdentifierValue()); - IWorkSession session = ensureSession(appId, clientPublicKey, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); + IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); Map payload = buildUserPayload(reqVO); String responseBody = executeJsonRequest(properties.getPaths().getUserInfo(), reqVO.getQueryParams(), appId, session, payload); @@ -96,9 +124,9 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { public IWorkOperationRespVO createWorkflow(IWorkWorkflowCreateReqVO reqVO) { assertConfigured(); String appId = resolveAppId(); - String clientPublicKey = resolveClientPublicKey(); + ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId()); - IWorkSession session = ensureSession(appId, clientPublicKey, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); + IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); MultiValueMap formData = buildCreateForm(reqVO); appendFormExtras(formData, reqVO.getFormExtras()); @@ -110,12 +138,12 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { public IWorkOperationRespVO voidWorkflow(IWorkWorkflowVoidReqVO reqVO) { assertConfigured(); String appId = resolveAppId(); - String clientPublicKey = resolveClientPublicKey(); + ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId()); if (!StringUtils.hasText(reqVO.getRequestId())) { throw ServiceExceptionUtil.exception(IWORK_USER_IDENTIFIER_MISSING); } - IWorkSession session = ensureSession(appId, clientPublicKey, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); + IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); MultiValueMap formData = buildVoidForm(reqVO); appendFormExtras(formData, reqVO.getFormExtras()); @@ -127,7 +155,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { if (!StringUtils.hasText(properties.getBaseUrl())) { throw ServiceExceptionUtil.exception(IWORK_BASE_URL_MISSING); } - if (!StringUtils.hasText(properties.getAppId()) || !StringUtils.hasText(properties.getClientPublicKey())) { + if (!StringUtils.hasText(properties.getAppId())) { throw ServiceExceptionUtil.exception(IWORK_CONFIGURATION_INVALID); } } @@ -150,12 +178,33 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { return StringUtils.trimWhitespace(value); } - private String resolveClientPublicKey() { - String value = properties.getClientPublicKey(); - if (!StringUtils.hasText(value)) { - throw ServiceExceptionUtil.exception(IWORK_CONFIGURATION_INVALID); + private ClientKeyPair resolveClientKeyPair(String appId, boolean forceRefresh) { + String configured = properties.getClientPublicKey(); + if (StringUtils.hasText(configured)) { + return new ClientKeyPair(StringUtils.trimWhitespace(configured), null); + } + if (!forceRefresh) { + ClientKeyPair cached = clientKeyCache.getIfPresent(appId); + if (cached != null) { + return cached; + } + } + ClientKeyPair generated = generateClientKeyPair(); + clientKeyCache.put(appId, generated); + return generated; + } + + private ClientKeyPair generateClientKeyPair() { + try { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair pair = generator.generateKeyPair(); + String publicKey = Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()); + String privateKey = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()); + return new ClientKeyPair(publicKey, privateKey); + } catch (NoSuchAlgorithmException ex) { + throw new ServiceException(IWORK_CONFIGURATION_INVALID.getCode(), "生成客户端 RSA 公钥失败: " + ex.getMessage()); } - return StringUtils.trimWhitespace(value); } private void ensureIdentifier(String identifierKey, String identifierValue) { @@ -164,7 +213,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { } } - private IWorkSession ensureSession(String appId, String clientPublicKey, String operatorUserId, boolean forceRefresh) { + private IWorkSession ensureSession(String appId, ClientKeyPair clientKeyPair, String operatorUserId, boolean forceRefresh) { SessionKey key = new SessionKey(appId, operatorUserId); Instant now = Instant.now(); if (!forceRefresh) { @@ -178,14 +227,14 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { if (!forceRefresh && cached != null && cached.isValid(now, properties.getToken().getRefreshAheadSeconds())) { return cached; } - IWorkSession session = createSession(appId, clientPublicKey, operatorUserId); + IWorkSession session = createSession(appId, clientKeyPair, operatorUserId, forceRefresh); sessionCache.put(key, session); return session; } } - private IWorkSession createSession(String appId, String clientPublicKey, String operatorUserId) { - RegistrationResult registration = register(appId, clientPublicKey); + private IWorkSession createSession(String appId, ClientKeyPair clientKeyPair, String operatorUserId, boolean forceRefreshRegistration) { + RegistrationState registration = obtainRegistration(appId, clientKeyPair, forceRefreshRegistration); String encryptedSecret = encryptWithPublicKey(registration.secret(), registration.spk()); String encryptedUserId = encryptWithPublicKey(operatorUserId, registration.spk()); String token = applyToken(appId, encryptedSecret); @@ -193,11 +242,32 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { return new IWorkSession(token, encryptedUserId, expiresAt, registration.spk()); } - private RegistrationResult register(String appId, String clientPublicKey) { + private RegistrationState obtainRegistration(String appId, ClientKeyPair clientKeyPair, boolean forceRefresh) { + if (!forceRefresh) { + RegistrationState cached = registrationCache.getIfPresent(appId); + if (cached != null && Objects.equals(cached.clientKeyPair().publicKey(), clientKeyPair.publicKey())) { + return cached; + } + } + String lockKey = ("iwork-registration::" + appId).intern(); + synchronized (lockKey) { + if (!forceRefresh) { + RegistrationState cached = registrationCache.getIfPresent(appId); + if (cached != null && Objects.equals(cached.clientKeyPair().publicKey(), clientKeyPair.publicKey())) { + return cached; + } + } + RegistrationState registration = register(appId, clientKeyPair); + registrationCache.put(appId, registration); + return registration; + } + } + + private RegistrationState register(String appId, ClientKeyPair clientKeyPair) { Request request = new Request.Builder() .url(resolveUrl(properties.getPaths().getRegister())) .header(properties.getHeaders().getAppId(), appId) - .header(properties.getHeaders().getClientPublicKey(), clientPublicKey) + .header(properties.getHeaders().getClientPublicKey(), clientKeyPair.publicKey()) .post(RequestBody.create(null, new byte[0])) .build(); String responseBody = executeRequest(request, IWORK_REGISTER_FAILED); @@ -207,7 +277,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { if (!StringUtils.hasText(secret) || !StringUtils.hasText(spk)) { throw ServiceExceptionUtil.exception(IWORK_REGISTER_FAILED, "返回缺少 secret 或 spk"); } - return new RegistrationResult(secret, spk); + return new RegistrationState(secret, spk, clientKeyPair); } private String applyToken(String appId, String encryptedSecret) { @@ -575,7 +645,10 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService { } } - private record RegistrationResult(String secret, String spk) { + private record RegistrationState(String secret, String spk, ClientKeyPair clientKeyPair) { + } + + private record ClientKeyPair(String publicKey, String privateKey) { } @Getter diff --git a/zt-module-system/zt-module-system-server/src/main/resources/application.yaml b/zt-module-system/zt-module-system-server/src/main/resources/application.yaml index 21863d2f..30293fe0 100644 --- a/zt-module-system/zt-module-system-server/src/main/resources/application.yaml +++ b/zt-module-system/zt-module-system-server/src/main/resources/application.yaml @@ -107,10 +107,11 @@ easy-trans: iwork: base-url: http://172.16.36.233:8080 - app-id: +# app-id: f47ac10b-58cc-4372-a567-0e02b2c3d479 + app-id: f47ac10b-58cc-4372-a567-0e02b2c3d479 client-public-key: - user-id: - workflow-id: + user-id: 9869 + workflow-id: 1753 paths: register: /api/ec/dev/auth/regist apply-token: /api/ec/dev/auth/applytoken @@ -169,6 +170,8 @@ xxl: job: executor: appname: ${spring.application.name} # 执行器 AppName + port: 0 + ip: 172.16.234.132 logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 accessToken: default_token # 执行器通讯TOKEN