From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master
---
jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsFileCenterCursorCodec.java | 137 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 137 insertions(+), 0 deletions(-)
diff --git a/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsFileCenterCursorCodec.java b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsFileCenterCursorCodec.java
new file mode 100644
index 0000000..46c0ee6
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsFileCenterCursorCodec.java
@@ -0,0 +1,137 @@
+package jnpf.dmsPermission;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.util.Base64;
+import java.util.UUID;
+
+@Component
+@RequiredArgsConstructor
+public class DmsFileCenterCursorCodec {
+ private static final String VERSION = "1";
+ private static final String HMAC_ALGORITHM = "HmacSHA256";
+
+ private final DmsPermissionProperties properties;
+
+ public String encode(String kind, String tenantId, String userId, UUID parentId,
+ String queryFingerprint, Cursor cursor) {
+ String payload = String.join("|",
+ VERSION,
+ field(kind),
+ field(tenantId),
+ field(userId),
+ field(parentId == null ? null : parentId.toString()),
+ field(queryFingerprint),
+ field(cursor.getName()),
+ field(uuid(cursor.getDocumentId())),
+ field(cursor.getVersionNo() == null ? null : cursor.getVersionNo().toString()),
+ field(uuid(cursor.getId())));
+ return base64(payload.getBytes(StandardCharsets.UTF_8)) + "." + base64(sign(payload));
+ }
+
+ public Cursor decode(String value, String expectedKind, String expectedTenantId,
+ String expectedUserId, UUID expectedParentId, String expectedFingerprint) {
+ if (value == null || value.trim().isEmpty()) {
+ return new Cursor(null, null, null, null);
+ }
+ try {
+ String[] token = value.split("\\.", -1);
+ if (token.length != 2) {
+ throw new IllegalArgumentException("cursor token parts");
+ }
+ String payload = new String(Base64.getUrlDecoder().decode(token[0]), StandardCharsets.UTF_8);
+ byte[] providedSignature = Base64.getUrlDecoder().decode(token[1]);
+ if (!MessageDigest.isEqual(providedSignature, sign(payload))) {
+ throw new IllegalArgumentException("cursor signature");
+ }
+ String[] parts = payload.split("\\|", -1);
+ if (parts.length != 10 || !VERSION.equals(parts[0])) {
+ throw new IllegalArgumentException("cursor payload");
+ }
+ String kind = unfield(parts[1]);
+ String tenantId = unfield(parts[2]);
+ String userId = unfield(parts[3]);
+ UUID parentId = uuid(unfield(parts[4]));
+ String fingerprint = unfield(parts[5]);
+ if (!expectedKind.equals(kind)
+ || !expectedTenantId.equals(tenantId)
+ || !expectedUserId.equals(userId)
+ || !java.util.Objects.equals(expectedParentId, parentId)
+ || !expectedFingerprint.equals(fingerprint)) {
+ throw new IllegalArgumentException("cursor context");
+ }
+ return new Cursor(
+ unfield(parts[6]),
+ uuid(unfield(parts[7])),
+ number(unfield(parts[8])),
+ uuid(unfield(parts[9])));
+ } catch (RuntimeException ex) {
+ throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST,
+ "鍒嗛〉 cursor 鏃犳晥鎴栦笉灞炰簬褰撳墠鐢ㄦ埛銆佺洰褰曞拰鏌ヨ鏉′欢");
+ }
+ }
+
+ public String fingerprint(String value) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ return base64(digest.digest((value == null ? "" : value).getBytes(StandardCharsets.UTF_8)));
+ } catch (GeneralSecurityException ex) {
+ throw new IllegalStateException("SHA-256 unavailable", ex);
+ }
+ }
+
+ private byte[] sign(String payload) {
+ String secret = properties.getCursorSigningSecret();
+ if (secret == null || secret.length() < 16) {
+ throw new IllegalStateException("dms.permission.cursor-signing-secret 鑷冲皯闇�瑕� 16 涓瓧绗�");
+ }
+ try {
+ Mac mac = Mac.getInstance(HMAC_ALGORITHM);
+ mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM));
+ return mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
+ } catch (GeneralSecurityException ex) {
+ throw new IllegalStateException("HMAC-SHA256 unavailable", ex);
+ }
+ }
+
+ private String field(String value) {
+ return value == null ? "" : base64(value.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private String unfield(String value) {
+ return value.isEmpty() ? null : new String(Base64.getUrlDecoder().decode(value), StandardCharsets.UTF_8);
+ }
+
+ private String base64(byte[] value) {
+ return Base64.getUrlEncoder().withoutPadding().encodeToString(value);
+ }
+
+ private String uuid(UUID value) {
+ return value == null ? null : value.toString();
+ }
+
+ private UUID uuid(String value) {
+ return value == null ? null : UUID.fromString(value);
+ }
+
+ private Long number(String value) {
+ return value == null ? null : Long.valueOf(value);
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class Cursor {
+ private String name;
+ private UUID documentId;
+ private Long versionNo;
+ private UUID id;
+ }
+}
--
Gitblit v1.8.0