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/DmsPermissionGuard.java | 199 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 199 insertions(+), 0 deletions(-)
diff --git a/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsPermissionGuard.java b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsPermissionGuard.java
new file mode 100644
index 0000000..66ef2a2
--- /dev/null
+++ b/jnpf-dms/jnpf-dms-biz/src/main/java/jnpf/dmsPermission/DmsPermissionGuard.java
@@ -0,0 +1,199 @@
+package jnpf.dmsPermission;
+
+import jnpf.base.UserInfo;
+import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
+import jnpf.dmsEntity.permission.entity.DmsPermissionUsageEntity;
+import jnpf.dmsEntity.permission.enums.DmsPermissionAction;
+import jnpf.dmsEntity.permission.form.PermissionDecisionForm;
+import jnpf.dmsEntity.permission.model.DmsResourcePath;
+import jnpf.dmsEntity.permission.model.DmsSubjectSet;
+import jnpf.dmsEntity.permission.vo.DmsPermissionDecisionVO;
+import jnpf.dmsMapper.DmsFilePermissionMapper;
+import jnpf.dmsMapper.DmsPermissionUsageMapper;
+import jnpf.dmsService.DmsPermissionDecisionService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.UUID;
+
+@Component
+@RequiredArgsConstructor
+public class DmsPermissionGuard {
+ private final DmsCurrentIdentity currentIdentity;
+ private final DmsPermissionDecisionService decisionService;
+ private final DmsFilePermissionMapper permissionMapper;
+ private final DmsPermissionUsageMapper usageMapper;
+ private final DmsResourceResolver resourceResolver;
+ private final DmsSubjectResolver subjectResolver;
+ private final DmsPermissionGroupMembershipResolver groupMembershipResolver;
+ private final DmsPermissionMatcher matcher;
+ private final DmsAccessExclusionResolver exclusionResolver;
+
+ @Transactional
+ public <T> T execute(DmsPermissionExecutionContext context, DmsPermissionOperation<T> operation) {
+ validateContext(context, operation);
+ UserInfo user = currentIdentity.requireUser();
+ String tenantId = currentIdentity.tenantId(user);
+ if (context.getOperationId() != null) {
+ DmsPermissionUsageEntity prior = usageMapper.selectByOperation(tenantId, user.getUserId(),
+ context.getOperationId());
+ if (prior != null) {
+ DmsResourcePath path = resourceResolver.resolve(tenantId, context.getResourceType(),
+ context.getResourceId());
+ ensureNotReadExcluded(context, tenantId, user.getUserId().trim(), path);
+ validateReplay(prior, context, user, tenantId, path);
+ return operation.replay();
+ }
+ }
+
+ PermissionDecisionForm request = new PermissionDecisionForm();
+ request.setResourceType(context.getResourceType());
+ request.setResourceId(context.getResourceId());
+ request.setActionCode(context.getActionCode());
+ DmsPermissionDecisionVO decision = decisionService.evaluate(request);
+ if (!decision.isAllowed()) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "褰撳墠鐢ㄦ埛娌℃湁鎵�闇�鐨� " + context.getActionCode() + " 鏉冮檺");
+ }
+ DmsFilePermissionEntity permission = permissionMapper.selectByTenantAndIdForUpdate(tenantId,
+ decision.getMatchedPermissionId());
+ if (permission == null) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED, "鍖归厤鏉冮檺宸蹭笉瀛樺湪");
+ }
+ if (context.getOperationId() != null) {
+ DmsPermissionUsageEntity concurrentPrior = usageMapper.selectByOperation(tenantId,
+ user.getUserId(), context.getOperationId());
+ if (concurrentPrior != null) {
+ DmsResourcePath path = resourceResolver.resolve(tenantId, context.getResourceType(),
+ context.getResourceId());
+ ensureNotReadExcluded(context, tenantId, user.getUserId().trim(), path);
+ validateReplay(concurrentPrior, context, user, tenantId, path);
+ return operation.replay();
+ }
+ }
+ DmsResourcePath path = resourceResolver.resolve(tenantId, context.getResourceType(),
+ context.getResourceId());
+ ensureNotReadExcluded(context, tenantId, user.getUserId().trim(), path);
+ validateLockedPermission(permission, context, user, tenantId, path);
+ if (permission.getMaxUseCount() == null) {
+ return operation.execute();
+ }
+ if (context.getOperationId() == null) {
+ throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST,
+ "鎵ц鎸夋鏉冮檺蹇呴』鎻愪緵 operationId");
+ }
+
+ Date sessionExpiresAt = normalizedSessionExpiry(context);
+ T result = operation.execute();
+ DmsPermissionUsageEntity usage = new DmsPermissionUsageEntity();
+ usage.setTenantId(tenantId);
+ usage.setPermissionId(permission.getId());
+ usage.setUserId(user.getUserId());
+ usage.setActionCode(context.getActionCode().name());
+ usage.setActualResourceType(context.getResourceType().name());
+ usage.setActualResourceId(context.getResourceId());
+ usage.setOperationId(context.getOperationId());
+ usage.setSessionExpiresAt(sessionExpiresAt);
+ try {
+ usageMapper.insert(usage);
+ } catch (DuplicateKeyException ex) {
+ throw DmsPermissionException.of(DmsPermissionError.IDEMPOTENCY_CONFLICT,
+ "operationId 宸茶骞跺彂璇锋眰浣跨敤锛岃鎸夌浉鍚� operationId 閲嶈瘯");
+ }
+ return result;
+ }
+
+ private void validateLockedPermission(DmsFilePermissionEntity permission,
+ DmsPermissionExecutionContext context, UserInfo user,
+ String tenantId, DmsResourcePath path) {
+ Date now = new Date();
+ if (!tenantId.equals(permission.getTenantId())
+ || !context.getActionCode().name().equals(permission.getActionCode())
+ || permission.getRevokedAt() != null
+ || permission.getValidFrom().after(now)
+ || (permission.getValidTo() != null && !now.before(permission.getValidTo()))) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "鍖归厤鏉冮檺鍦ㄦ墽琛屽墠宸插け鏁�");
+ }
+ if (permission.getMaxUseCount() != null
+ && permission.getUsedCount() >= permission.getMaxUseCount()) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_EXHAUSTED,
+ "鍖归厤鏉冮檺鐨勪娇鐢ㄦ鏁板凡鐢ㄥ敖");
+ }
+ DmsSubjectSet subjects = subjectResolver.resolve(user);
+ if ("GROUP".equals(permission.getPrincipalType())) {
+ groupMembershipResolver.resolve(Collections.singleton(permission.getPrincipalId()), subjects);
+ }
+ if (!matcher.matches(permission, path, subjects)) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "褰撳墠涓讳綋鎴栬祫婧愬湪鎵ц鍓嶅凡涓嶅啀鍖归厤鏉冮檺");
+ }
+ }
+
+ private void validateReplay(DmsPermissionUsageEntity prior, DmsPermissionExecutionContext context,
+ UserInfo user, String tenantId, DmsResourcePath path) {
+ if (!context.getResourceType().name().equals(prior.getActualResourceType())
+ || !context.getResourceId().equals(prior.getActualResourceId())
+ || !context.getActionCode().name().equals(prior.getActionCode())) {
+ throw DmsPermissionException.of(DmsPermissionError.IDEMPOTENCY_CONFLICT,
+ "operationId 宸茬敤浜庝笉鍚岃祫婧愭垨鍔ㄤ綔");
+ }
+ if ((context.getActionCode() == DmsPermissionAction.READ
+ || context.getActionCode() == DmsPermissionAction.UPDATE)
+ && prior.getSessionExpiresAt() != null && !new Date().before(prior.getSessionExpiresAt())) {
+ throw DmsPermissionException.of(DmsPermissionError.IDEMPOTENCY_CONFLICT,
+ "operationId 瀵瑰簲鐨勬潈闄愪細璇濆凡杩囨湡锛岃浣跨敤鏂扮殑 operationId");
+ }
+ DmsFilePermissionEntity permission = permissionMapper.selectByTenantAndId(tenantId,
+ prior.getPermissionId());
+ Date now = new Date();
+ if (permission == null || permission.getRevokedAt() != null || permission.getValidFrom().after(now)
+ || (permission.getValidTo() != null && !now.before(permission.getValidTo()))) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "鍘熸寜娆℃潈闄愬凡鎾ら攢鎴栦笉鍦ㄦ湁鏁堟湡鍐�");
+ }
+ DmsSubjectSet subjects = subjectResolver.resolve(user);
+ if ("GROUP".equals(permission.getPrincipalType())) {
+ groupMembershipResolver.resolve(Collections.singleton(permission.getPrincipalId()), subjects);
+ }
+ if (!permission.getActionCode().equals(context.getActionCode().name())
+ || !matcher.matches(permission, path, subjects)) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "褰撳墠涓讳綋鎴栬祫婧愪笉鍐嶅尮閰嶅師鎸夋鏉冮檺");
+ }
+ }
+
+ private void ensureNotReadExcluded(DmsPermissionExecutionContext context, String tenantId,
+ String userId, DmsResourcePath path) {
+ if (context.getActionCode() == DmsPermissionAction.READ
+ && exclusionResolver.isExcluded(tenantId, userId, path)) {
+ throw DmsPermissionException.of(DmsPermissionError.PERMISSION_DENIED,
+ "褰撳墠鐢ㄦ埛鐨� READ 鏉冮檺宸茶鎺掗櫎");
+ }
+ }
+
+ private Date normalizedSessionExpiry(DmsPermissionExecutionContext context) {
+ if (context.getActionCode() != DmsPermissionAction.READ
+ && context.getActionCode() != DmsPermissionAction.UPDATE) {
+ return null;
+ }
+ Date expiry = context.getSessionExpiresAt();
+ if (expiry != null && !expiry.after(new Date())) {
+ throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST,
+ "sessionExpiresAt 蹇呴』鏅氫簬褰撳墠鏃堕棿");
+ }
+ return expiry;
+ }
+
+ private <T> void validateContext(DmsPermissionExecutionContext context, DmsPermissionOperation<T> operation) {
+ if (context == null || operation == null || context.getResourceType() == null
+ || context.getResourceId() == null || context.getActionCode() == null) {
+ throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST,
+ "PermissionGuard 鎵ц涓婁笅鏂囦笉瀹屾暣");
+ }
+ }
+}
--
Gitblit v1.8.0