diff --git a/src/main/java/com/cisd/tms/common/config/WebMvcConfig.java b/src/main/java/com/cisd/tms/common/config/WebMvcConfig.java index 5e127a7..8faa830 100644 --- a/src/main/java/com/cisd/tms/common/config/WebMvcConfig.java +++ b/src/main/java/com/cisd/tms/common/config/WebMvcConfig.java @@ -96,7 +96,8 @@ public class WebMvcConfig implements WebMvcConfigurer { "/api/v1/auth/password-login", "/api/v1/auth/ukey-login", "/api/v1/auth/ukey-login/randoms", - "/api/v1/auth/captcha" + "/api/v1/auth/captcha", + "/api/v1/auth/super-admin/ukeys/issue-sign" ); registry.addInterceptor(internalApiReplayInterceptor) diff --git a/src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java b/src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java index 42eca99..03088db 100644 --- a/src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java +++ b/src/main/java/com/cisd/tms/modules/auth/controller/AuthAdminController.java @@ -1,8 +1,9 @@ package com.cisd.tms.modules.auth.controller; import com.cisd.tms.common.api.ApiResponse; +import com.cisd.tms.common.enums.ErrorCode; +import com.cisd.tms.common.exception.BizException; import com.cisd.tms.modules.auth.dto.AdminChangePasswordRequest; -import com.cisd.tms.modules.auth.dto.UkeyBindRequest; import com.cisd.tms.modules.auth.enums.AuthLevel; import com.cisd.tms.modules.auth.enums.RoleCode; import com.cisd.tms.modules.auth.security.RequireInternalAuth; @@ -24,14 +25,14 @@ import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/auth") @RequiredArgsConstructor -@ReplayProtected @Tag(name = "认证管理", description = "角色启用、密码重置与 UKey 绑定接口") public class AuthAdminController { private final AuthAdminService authAdminService; @PostMapping("/roles/{roleCode}/enable") - @Operation(summary = "启用角色", description = "仅允许 KEY_ADMIN FULL 会话启用目标角色。") + @Operation(summary = "启用角色", description = "仅允许 SUPER_ADMIN FULL 会话启用目标角色。") + @ReplayProtected @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) @AuditedOperation(module = ModuleCode.AUTH, action = ActionType.ENABLE, summary = "启用角色") public ApiResponse enableRole(@PathVariable("roleCode") String roleCode, HttpServletRequest request) { @@ -44,7 +45,8 @@ public class AuthAdminController { } @PostMapping("/roles/{roleCode}/reset-password") - @Operation(summary = "重置角色密码", description = "仅允许 KEY_ADMIN FULL 会话重置目标角色密码。") + @Operation(summary = "重置角色密码", description = "仅允许 SUPER_ADMIN FULL 会话重置目标角色密码。") + @ReplayProtected @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) @AuditedOperation(module = ModuleCode.AUTH, action = ActionType.RESET, summary = "重置角色密码") public ApiResponse resetPassword(@PathVariable("roleCode") String roleCode, HttpServletRequest request) { @@ -58,6 +60,7 @@ public class AuthAdminController { @PostMapping("/roles/{roleCode}/full-accounts/{uid}/change-password") @Operation(summary = "管理员修改 FULL 账户口令", description = "管理员为目标角色指定 UKey 席位账号设置新口令。") + @ReplayProtected // @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) @AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 FULL 账户口令") public ApiResponse changeFullAccountPassword( @@ -79,6 +82,7 @@ public class AuthAdminController { @PostMapping("/roles/{roleCode}/limited-accounts/{username}/change-password") @Operation(summary = "管理员修改 LIMITED 账户口令", description = "管理员为目标角色指定独立用户账号设置新口令。") + @ReplayProtected // @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) @AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 LIMITED 账户口令") public ApiResponse changeLimitedAccountPassword( @@ -98,35 +102,31 @@ public class AuthAdminController { return ApiResponse.success(); } - @PostMapping("/roles/{roleCode}/ukeys/bind") - @Operation(summary = "绑定角色 UKey", description = "仅允许 KEY_ADMIN FULL 会话登记目标角色的 UKey 绑定信息。") - @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) - @AuditedOperation(module = ModuleCode.AUTH, action = ActionType.BIND, summary = "绑定角色 UKey") - public ApiResponse bindUkey( - @PathVariable("roleCode") String roleCode, - @Valid @RequestBody UkeyBindRequest request, - HttpServletRequest httpRequest + @PostMapping("/super-admin/ukeys/issue-sign") + @Operation(summary = "签发并绑定超级管理员 UKey", description = "系统初始化后登记超级管理员 UKey,不要求已有登录态。") + public ApiResponse issueSuperAdminUkeyBindingSign( + @Valid @RequestBody UKeySignDTO request ) { - authAdminService.bindIssuedUkey( - (String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE), - (String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL), - roleCode, - request.getUid(), - request.getUkeySerial(), - request.getPubKey(), - request.getIssuerSignature() - ); - return ApiResponse.success(); + return ApiResponse.success(authAdminService.issueUkeyBindingSign( + null, + null, + RoleCode.SUPER_ADMIN.getCode(), + request + )); } @PostMapping("/roles/{roleCode}/ukeys/issue-sign") - @Operation(summary = "生成 UKey 发行签名", description = "按旧绑定流程为目标角色 UKey 材料生成发行签名。") -// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.LIMITED) + @Operation(summary = "签发并绑定其他管理员 UKey", description = "仅允许 SUPER_ADMIN FULL 会话登记其他管理员的 UKey。") + @ReplayProtected + @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL) public ApiResponse issueUkeyBindingSign( @PathVariable("roleCode") String roleCode, - @RequestBody UKeySignDTO request, + @Valid @RequestBody UKeySignDTO request, HttpServletRequest httpRequest ) { + if (RoleCode.SUPER_ADMIN.getCode().equals(roleCode)) { + throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "use super-admin ukey binding endpoint"); + } return ApiResponse.success(authAdminService.issueUkeyBindingSign( (String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE), (String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL), diff --git a/src/main/java/com/cisd/tms/modules/auth/dto/UkeyBindRequest.java b/src/main/java/com/cisd/tms/modules/auth/dto/UkeyBindRequest.java deleted file mode 100644 index 8730664..0000000 --- a/src/main/java/com/cisd/tms/modules/auth/dto/UkeyBindRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.cisd.tms.modules.auth.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.NotBlank; - -@Schema(description = "UKey 绑定请求") -public class UkeyBindRequest { - - @NotNull(message = "uid is required") - @Schema(description = "角色内固定席位编号", example = "1") - private Integer uid; - - @NotBlank(message = "ukeySerial is required") - @Schema(description = "UKey 序列号", example = "UK-001") - private String ukeySerial; - - @NotBlank(message = "pubKey is required") - @Schema(description = "UKey 公钥") - private String pubKey; - - @NotBlank(message = "issuerSignature is required") - @Schema(description = "认证公钥签名值") - private String issuerSignature; - - public Integer getUid() { - return uid; - } - - public void setUid(Integer uid) { - this.uid = uid; - } - - public String getUkeySerial() { - return ukeySerial; - } - - public void setUkeySerial(String ukeySerial) { - this.ukeySerial = ukeySerial; - } - - public String getPubKey() { - return pubKey; - } - - public void setPubKey(String pubKey) { - this.pubKey = pubKey; - } - - public String getIssuerSignature() { - return issuerSignature; - } - - public void setIssuerSignature(String issuerSignature) { - this.issuerSignature = issuerSignature; - } -} diff --git a/src/main/java/com/cisd/tms/modules/auth/mapper/RoleUkeyBindingMapper.java b/src/main/java/com/cisd/tms/modules/auth/mapper/RoleUkeyBindingMapper.java index b7001da..212471d 100644 --- a/src/main/java/com/cisd/tms/modules/auth/mapper/RoleUkeyBindingMapper.java +++ b/src/main/java/com/cisd/tms/modules/auth/mapper/RoleUkeyBindingMapper.java @@ -9,7 +9,7 @@ import org.apache.ibatis.annotations.Param; @Mapper public interface RoleUkeyBindingMapper extends BaseMapperX { - RoleUkeyBindingEntity selectActiveByRoleCodeAndUid(@Param("roleCode") String roleCode, @Param("uid") Integer uid); + List selectActiveByRoleCodeAndUid(@Param("roleCode") String roleCode, @Param("uid") Integer uid); List selectActiveByRoleCode(@Param("roleCode") String roleCode); } diff --git a/src/main/java/com/cisd/tms/modules/auth/repository/RoleUkeyBindingRepository.java b/src/main/java/com/cisd/tms/modules/auth/repository/RoleUkeyBindingRepository.java index 22feb11..42163f1 100644 --- a/src/main/java/com/cisd/tms/modules/auth/repository/RoleUkeyBindingRepository.java +++ b/src/main/java/com/cisd/tms/modules/auth/repository/RoleUkeyBindingRepository.java @@ -2,11 +2,10 @@ package com.cisd.tms.modules.auth.repository; import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity; import java.util.List; -import java.util.Optional; public interface RoleUkeyBindingRepository { - Optional findActiveByRoleCodeAndUid(String roleCode, Integer uid); + List findActiveByRoleCodeAndUid(String roleCode, Integer uid); List findActiveByRoleCode(String roleCode); diff --git a/src/main/java/com/cisd/tms/modules/auth/repository/impl/RoleUkeyBindingRepositoryImpl.java b/src/main/java/com/cisd/tms/modules/auth/repository/impl/RoleUkeyBindingRepositoryImpl.java index ea03979..56ff716 100644 --- a/src/main/java/com/cisd/tms/modules/auth/repository/impl/RoleUkeyBindingRepositoryImpl.java +++ b/src/main/java/com/cisd/tms/modules/auth/repository/impl/RoleUkeyBindingRepositoryImpl.java @@ -4,7 +4,6 @@ import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity; import com.cisd.tms.modules.auth.mapper.RoleUkeyBindingMapper; import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository; import java.util.List; -import java.util.Optional; import org.springframework.stereotype.Repository; @Repository @@ -17,8 +16,8 @@ public class RoleUkeyBindingRepositoryImpl implements RoleUkeyBindingRepository } @Override - public Optional findActiveByRoleCodeAndUid(String roleCode, Integer uid) { - return Optional.ofNullable(roleUkeyBindingMapper.selectActiveByRoleCodeAndUid(roleCode, uid)); + public List findActiveByRoleCodeAndUid(String roleCode, Integer uid) { + return roleUkeyBindingMapper.selectActiveByRoleCodeAndUid(roleCode, uid); } @Override diff --git a/src/main/java/com/cisd/tms/modules/auth/service/AuthAdminService.java b/src/main/java/com/cisd/tms/modules/auth/service/AuthAdminService.java index 8957bd6..afb6fd4 100644 --- a/src/main/java/com/cisd/tms/modules/auth/service/AuthAdminService.java +++ b/src/main/java/com/cisd/tms/modules/auth/service/AuthAdminService.java @@ -27,26 +27,6 @@ public interface AuthAdminService { String newPassword ); - void bindUkey( - String operatorRoleCode, - String operatorAuthLevel, - String targetRoleCode, - Integer uid, - String ukeySerial, - String ukeyPubkey, - String issuerSign - ); - - void bindIssuedUkey( - String operatorRoleCode, - String operatorAuthLevel, - String targetRoleCode, - Integer uid, - String ukeySerial, - String ukeyPubkey, - String issuerSign - ); - UKeySignResult issueUkeyBindingSign( String operatorRoleCode, String operatorAuthLevel, diff --git a/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthAdminServiceImpl.java b/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthAdminServiceImpl.java index 8afcede..52ae1da 100644 --- a/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthAdminServiceImpl.java +++ b/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthAdminServiceImpl.java @@ -174,10 +174,7 @@ public class AuthAdminServiceImpl implements AuthAdminService { authUserAccountRepository.update(account); } - @Override - public void bindUkey( - String operatorRoleCode, - String operatorAuthLevel, + private void bindUkey( String targetRoleCode, Integer uid, String ukeySerial, @@ -185,15 +182,11 @@ public class AuthAdminServiceImpl implements AuthAdminService { String issuerSign ) { RoleCode targetRole = resolveRoleCode(targetRoleCode); - if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) { - throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement"); - } + validateRoleUid(targetRole, uid); authFullAccountRepository.findByRoleCodeAndUid(targetRoleCode, uid) .orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role full account not found")); - RoleUkeyBindingEntity binding = roleUkeyBindingRepository - .findActiveByRoleCodeAndUid(targetRoleCode, uid) - .orElseGet(RoleUkeyBindingEntity::new); + RoleUkeyBindingEntity binding = findExistingActiveBinding(targetRoleCode, uid, ukeySerial, ukeyPubkey); if (binding.getId() == null) { binding.setId((long) Math.abs(Objects.hash(targetRoleCode, uid, ukeySerial, TraceIdUtil.newTraceId()))); binding.setRoleCode(targetRoleCode); @@ -208,19 +201,17 @@ public class AuthAdminServiceImpl implements AuthAdminService { roleUkeyBindingRepository.update(binding); } - @Override - public void bindIssuedUkey( - String operatorRoleCode, - String operatorAuthLevel, - String targetRoleCode, + private RoleUkeyBindingEntity findExistingActiveBinding( + String roleCode, Integer uid, String ukeySerial, - String ukeyPubkey, - String issuerSign + String ukeyPubkey ) { - resolveRoleCode(targetRoleCode); - verifyIssuerSignature(targetRoleCode, ukeyPubkey, uid, issuerSign); - bindUkey(operatorRoleCode, operatorAuthLevel, targetRoleCode, uid, ukeySerial, ukeyPubkey, issuerSign); + return roleUkeyBindingRepository.findActiveByRoleCodeAndUid(roleCode, uid).stream() + .filter(binding -> Objects.equals(binding.getUkeySerial(), ukeySerial) + || Objects.equals(binding.getUkeyPubkey(), ukeyPubkey)) + .findFirst() + .orElseGet(RoleUkeyBindingEntity::new); } @Override @@ -231,18 +222,28 @@ public class AuthAdminServiceImpl implements AuthAdminService { UKeySignDTO request ) { RoleCode targetRole = resolveRoleCode(targetRoleCode); + validateRoleUid(targetRole, request.getUid()); String authKeyPair = lmkService.exportIkPublicKeyHex(); UKeySignDTO dto = new UKeySignDTO(); dto.setPubKey(request.getPubKey()); - dto.setRole(targetRole.getCode()); dto.setUid(request.getUid()); - String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair)); + String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair, targetRole.getCode())); MasterKeyBackupPacket backupPacket = null; + if (request.getUkeySerial() == null || request.getUkeySerial().isBlank()) { + throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "ukeySerial is required"); + } if (RoleCode.SUPER_ADMIN == targetRole) { backupPacket = lmkService.buildBackupPacket(request.getUid()); } + bindUkey( + targetRole.getCode(), + request.getUid(), + request.getUkeySerial(), + request.getPubKey(), + signValue + ); return UKeySignResult.builder() .sign(signValue) .backupPacket(backupPacket) @@ -295,6 +296,12 @@ public class AuthAdminServiceImpl implements AuthAdminService { .orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role account not found")); } + private void validateRoleUid(RoleCode targetRole, Integer uid) { + if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) { + throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement"); + } + } + private RoleCode resolveRoleCode(String roleCode) { return List.of(RoleCode.values()).stream() .filter(item -> item.getCode().equals(roleCode)) @@ -306,24 +313,9 @@ public class AuthAdminServiceImpl implements AuthAdminService { return LocalDateTime.ofInstant(clock.instant(), ZoneOffset.UTC); } - private void verifyIssuerSignature(String targetRoleCode, String ukeyPubkey, Integer uid, String issuerSign) { + private String toIssuePayload(UKeySignDTO dto, String authKeyPair, String roleCode) { try { - UKeySignDTO dto = new UKeySignDTO(); - dto.setPubKey(ukeyPubkey); - dto.setRole(resolveRoleCode(targetRoleCode).getCode()); - dto.setUid(uid); - String authKeyPair = lmkService.exportIkPublicKeyHex(); - lmkService.verifyIk(toIssuePayload(dto, authKeyPair), issuerSign); - } catch (BizException ex) { - throw ex; - } catch (RuntimeException ex) { - throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey issuer signature verification failed"); - } - } - - private String toIssuePayload(UKeySignDTO dto, String authKeyPair) { - try { - return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair)); + return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair, roleCode)); } catch (JsonProcessingException ex) { throw new IllegalStateException("serialize ukey issue payload failed", ex); } diff --git a/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthServiceImpl.java b/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthServiceImpl.java index 8145275..4f97318 100644 --- a/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/cisd/tms/modules/auth/service/impl/AuthServiceImpl.java @@ -152,14 +152,8 @@ public class AuthServiceImpl implements AuthService { RoleCode roleCode = RoleCode.valueOf(request.getRoleCode()); List activeBindings = roleUkeyBindingRepository.findActiveByRoleCode(roleCode.getCode()); validateUkeyCount(roleCode, activeBindings, request.getLoginFactors()); - Map bindingsByUid = activeBindings.stream() - .collect(Collectors.toMap(RoleUkeyBindingEntity::getUid, item -> item, (left, right) -> left, java.util.LinkedHashMap::new)); - Set requestUids = request.getLoginFactors().stream() - .map(UkeyLoginProof::getUid) - .collect(Collectors.toSet()); - if (requestUids.size() != request.getLoginFactors().size() || !bindingsByUid.keySet().containsAll(requestUids)) { - throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role"); - } + Map> bindingsByUid = activeBindings.stream() + .collect(Collectors.groupingBy(RoleUkeyBindingEntity::getUid)); ukeyLoginRandomService.assertIssued( roleCode.getCode(), request.getLoginFactors().stream().map(UkeyLoginProof::getServerRandom).toList() @@ -167,10 +161,10 @@ public class AuthServiceImpl implements AuthService { String authKeyPair = lmkService.exportIkPublicKeyHex(); List matchedSerials = new ArrayList<>(); for (UkeyLoginProof proof : request.getLoginFactors()) { - RoleUkeyBindingEntity binding = bindingsByUid.get(proof.getUid()); - if (binding == null || !binding.getUkeyPubkey().equals(proof.getPubKey())) { - throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role"); - } + RoleUkeyBindingEntity binding = bindingsByUid.getOrDefault(proof.getUid(), List.of()).stream() + .filter(item -> item.getUkeyPubkey().equals(proof.getPubKey())) + .findFirst() + .orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role")); compatUkeyVerifier.verifyIssuedBinding(buildIssuePayload(request.getRoleCode(), proof, authKeyPair), proof.getIssueSignature()); compatUkeyVerifier.verifyLoginSignature(proof.getPubKey(), proof.getLoginPayload(), proof.getLoginSignature()); matchedSerials.add(binding.getUkeySerial()); @@ -503,16 +497,20 @@ public class AuthServiceImpl implements AuthService { } List bindings = roleUkeyBindingRepository.findActiveByRoleCode(roleAccount.getRoleCode()); - if (bindings.size() != requiredUkeyCount) { - throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement"); - } - Set boundSerials = bindings.stream() .map(RoleUkeyBindingEntity::getUkeySerial) .collect(Collectors.toSet()); if (!boundSerials.containsAll(uniqueSerials)) { throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey verification failed"); } + long matchedUidCount = bindings.stream() + .filter(binding -> uniqueSerials.contains(binding.getUkeySerial())) + .map(RoleUkeyBindingEntity::getUid) + .distinct() + .count(); + if (matchedUidCount != requiredUkeyCount) { + throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement"); + } return authPolicyService.resolveAuthLevel(AuthMethod.UKEY); } @@ -524,7 +522,17 @@ public class AuthServiceImpl implements AuthService { if (proofs == null || proofs.size() != authPolicyService.requiredUkeyCount(roleCode)) { throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement"); } - if (activeBindings.size() != authPolicyService.requiredUkeyCount(roleCode)) { + Set requestUids = proofs.stream() + .map(UkeyLoginProof::getUid) + .collect(Collectors.toSet()); + int requiredCount = authPolicyService.requiredUkeyCount(roleCode); + if (requestUids.size() != requiredCount) { + throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement"); + } + Set activeUids = activeBindings.stream() + .map(RoleUkeyBindingEntity::getUid) + .collect(Collectors.toSet()); + if (!activeUids.containsAll(requestUids)) { throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement"); } } @@ -532,10 +540,9 @@ public class AuthServiceImpl implements AuthService { private String buildIssuePayload(String roleCode, UkeyLoginProof proof, String authKeyPair) { UKeySignDTO dto = new UKeySignDTO(); dto.setPubKey(proof.getPubKey()); - dto.setRole(RoleCode.valueOf(roleCode).getCode()); dto.setUid(proof.getUid()); try { - return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair)); + return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair, RoleCode.valueOf(roleCode).getCode())); } catch (JsonProcessingException ex) { throw new IllegalStateException("serialize ukey issue payload failed", ex); } diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java index 0b952db..6668534 100644 --- a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java @@ -1,5 +1,7 @@ package com.cisd.tms.modules.mk.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; /** @@ -10,16 +12,19 @@ public class UKeySignDTO { /** * UKey 公钥 */ + @NotBlank(message = "pubKey is required") private String pubKey; - + /** - * 签名的角色 + * UKey 序列号。发行签名接口会直接完成绑定,因此必传。 */ - private String role; + @NotBlank(message = "ukeySerial is required") + private String ukeySerial; /** * 角色内固定席位编号。 */ + @NotNull(message = "uid is required") private Integer uid; } diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java index 4f208dc..fb7c606 100644 --- a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java @@ -27,11 +27,11 @@ public class UKeySignEntity { */ private Integer uid; - public static UKeySignEntity getInstance(UKeySignDTO uKeySignDTO,String authKeyPair){ + public static UKeySignEntity getInstance(UKeySignDTO uKeySignDTO, String authKeyPair, String role) { return UKeySignEntity.builder() .pubKey(uKeySignDTO.getPubKey()) .authKeyPair(authKeyPair) - .role(uKeySignDTO.getRole()) + .role(role) .uid(uKeySignDTO.getUid()) .build(); } diff --git a/src/main/resources/db/migration/V1__tms_schema_full.sql b/src/main/resources/db/migration/V1__tms_schema_full.sql index 6146194..12b8214 100644 --- a/src/main/resources/db/migration/V1__tms_schema_full.sql +++ b/src/main/resources/db/migration/V1__tms_schema_full.sql @@ -262,7 +262,8 @@ CREATE TABLE IF NOT EXISTS tms_role_ukey_binding ( unbound_at DATETIME(3) NULL, create_time DATETIME(3) NOT NULL, update_time DATETIME(3) NOT NULL, - UNIQUE KEY uk_tms_role_ukey_binding_role_uid (role_code, uid), + UNIQUE KEY uk_tms_role_ukey_binding_role_uid_serial (role_code, uid, ukey_serial), + KEY idx_tms_role_ukey_binding_role_uid_status (role_code, uid, status), KEY idx_tms_role_ukey_binding_role_status (role_code, status) ); diff --git a/src/main/resources/mapper/auth/RoleUkeyBindingMapper.xml b/src/main/resources/mapper/auth/RoleUkeyBindingMapper.xml index 6d50414..26a35c7 100644 --- a/src/main/resources/mapper/auth/RoleUkeyBindingMapper.xml +++ b/src/main/resources/mapper/auth/RoleUkeyBindingMapper.xml @@ -34,7 +34,7 @@ WHERE role_code = #{roleCode} AND uid = #{uid} AND status = 'ACTIVE' - LIMIT 1 + ORDER BY bound_at DESC, id DESC