feat: gate overseas receiving and harden phase4 workflows
This commit is contained in:
@@ -3,12 +3,16 @@ package com.baiyue.auth.controller;
|
||||
import com.baiyue.auth.dto.AccountManagementDtos.*;
|
||||
import com.baiyue.auth.service.AccountManagementService;
|
||||
import com.baiyue.auth.service.AuthService;
|
||||
import com.baiyue.auth.service.OverseasSessionValidationService;
|
||||
import com.baiyue.auth.dto.OverseasSessionValidationDtos;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -18,11 +22,14 @@ import java.security.MessageDigest;
|
||||
/** 仅供可信内部服务调用的账号管理接口。 */
|
||||
@RestController
|
||||
@RequestMapping("/internal/auth/accounts")
|
||||
@Tag(name = "Internal Auth / 内部认证", description = "Service-token protected internal account and overseas PDA session APIs")
|
||||
public class InternalAccountController {
|
||||
@Resource
|
||||
private AccountManagementService service;
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
@Resource
|
||||
private OverseasSessionValidationService overseasSessionValidationService;
|
||||
|
||||
@Value("${baiyue.security.internal-service-token}")
|
||||
private String configuredToken;
|
||||
@@ -64,6 +71,15 @@ public class InternalAccountController {
|
||||
@Valid @RequestBody RevokeAccountSessionsRequest request) {
|
||||
verify(token); service.revokeSessions(request.accountIds()); return ApiResponse.success(null);
|
||||
}
|
||||
@PostMapping("/overseas-session/validate")
|
||||
@Operation(summary = "Validate verified overseas PDA claims / 校验已验签的海外PDA声明",
|
||||
description = "Accepts only claims extracted after Gateway JWT verification; raw tokens and user credentials are forbidden.")
|
||||
public ApiResponse<OverseasSessionValidationDtos.Response> validateOverseasSession(
|
||||
@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||
@Valid @RequestBody OverseasSessionValidationDtos.Request request) {
|
||||
verify(token);
|
||||
return ApiResponse.success(overseasSessionValidationService.validate(request));
|
||||
}
|
||||
@PutMapping("/{id}/profile")
|
||||
public ApiResponse<Void> profile(@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
|
||||
@PathVariable Long id, @Valid @RequestBody UpdateAccountProfileRequest request) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baiyue.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/** 海外 PDA 已验签身份的实时会话校验契约;绝不接收 raw JWT 或用户凭据。 */
|
||||
public final class OverseasSessionValidationDtos {
|
||||
private OverseasSessionValidationDtos() { }
|
||||
|
||||
@Schema(name = "OverseasSessionValidationRequest",
|
||||
description = "Claims extracted only after overseas Gateway verifies the JWT signature")
|
||||
public record Request(
|
||||
@NotNull Long userId,
|
||||
@NotNull Integer tokenVersion,
|
||||
Long overseasWarehouseId) { }
|
||||
|
||||
@Schema(name = "OverseasSessionValidationResponse")
|
||||
public record Response(boolean valid, FailureReason failureReason) {
|
||||
public static Response accepted() { return new Response(true, null); }
|
||||
public static Response rejected(FailureReason reason) { return new Response(false, reason); }
|
||||
}
|
||||
|
||||
public enum FailureReason {
|
||||
ACCOUNT_INVALID,
|
||||
TOKEN_VERSION_MISMATCH,
|
||||
WAREHOUSE_UNBOUND,
|
||||
WAREHOUSE_MISMATCH
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.baiyue.auth.service;
|
||||
|
||||
import com.baiyue.auth.client.AuthorizationLoader;
|
||||
import com.baiyue.auth.dto.OverseasSessionValidationDtos.FailureReason;
|
||||
import com.baiyue.auth.dto.OverseasSessionValidationDtos.Request;
|
||||
import com.baiyue.auth.dto.OverseasSessionValidationDtos.Response;
|
||||
import com.baiyue.auth.entity.AuthAccount;
|
||||
import com.baiyue.auth.repository.AuthAccountRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** 实时核对海外 PDA token 的账号版本与当前 IAM 仓绑定。 */
|
||||
@Service
|
||||
public class OverseasSessionValidationService {
|
||||
@Resource private AuthAccountRepository accounts;
|
||||
@Resource private AuthorizationLoader authorizationLoader;
|
||||
|
||||
public Response validate(Request request) {
|
||||
AuthAccount account = accounts.findById(request.userId());
|
||||
if (account == null || !"ACTIVE".equals(account.getAccountStatus())) {
|
||||
return Response.rejected(FailureReason.ACCOUNT_INVALID);
|
||||
}
|
||||
int currentVersion = account.getTokenVersion() == null ? 0 : account.getTokenVersion();
|
||||
if (currentVersion != request.tokenVersion()) {
|
||||
return Response.rejected(FailureReason.TOKEN_VERSION_MISMATCH);
|
||||
}
|
||||
Long currentWarehouseId = authorizationLoader.load(account).getOverseasWarehouseId();
|
||||
if (currentWarehouseId == null || request.overseasWarehouseId() == null) {
|
||||
return Response.rejected(FailureReason.WAREHOUSE_UNBOUND);
|
||||
}
|
||||
if (!Objects.equals(currentWarehouseId, request.overseasWarehouseId())) {
|
||||
return Response.rejected(FailureReason.WAREHOUSE_MISMATCH);
|
||||
}
|
||||
return Response.accepted();
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class OverseasTokenService {
|
||||
.claim("app_code", authorization.getAppCode()).claim("client_type", authorization.getClientType())
|
||||
.claim("roles", authorization.getRoles())
|
||||
.claim("authorities", overseasAuthorities(authorization.getPermissions()))
|
||||
.claim("token_version", account.getTokenVersion() == null ? 0 : account.getTokenVersion())
|
||||
.claim("permission_version", authorization.getPermissionVersion() == null ? 0L : authorization.getPermissionVersion());
|
||||
if (authorization.getOverseasWarehouseId() != null) {
|
||||
claimsBuilder.claim("overseas_warehouse_id", authorization.getOverseasWarehouseId());
|
||||
|
||||
Reference in New Issue
Block a user