From 4f6a9ca00bae3c36e4fd37c14de6532e0c16dda6 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 21 May 2025 16:18:25 +0800 Subject: [PATCH 01/37] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dminio=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=B7=AF=E5=BE=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OssManagementBlobNamingNormalizer.cs | 6 +-- .../OssManagementBlobProvider.cs | 20 +++----- .../OssManagement/Minio/MinioOssContainer.cs | 50 ++++++++++++++----- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobNamingNormalizer.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobNamingNormalizer.cs index 0e9e338d7..9e7b60f63 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobNamingNormalizer.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobNamingNormalizer.cs @@ -1,5 +1,4 @@ -using System; -using Volo.Abp.BlobStoring; +using Volo.Abp.BlobStoring; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.BlobStoring.OssManagement; @@ -13,8 +12,7 @@ public class OssManagementBlobNamingNormalizer : IBlobNamingNormalizer, ITransie public virtual string NormalizeContainerName(string containerName) { - // 尾部添加反斜杠 - return NormalizeName(containerName).EnsureEndsWith('/'); + return NormalizeName(containerName); } protected virtual string NormalizeName(string name) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobProvider.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobProvider.cs index c14164118..4e2af27be 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobProvider.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.BlobStoring.OssManagement/LINGYUN/Abp/BlobStoring/OssManagement/OssManagementBlobProvider.cs @@ -26,10 +26,9 @@ public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency public override async Task DeleteAsync(BlobProviderDeleteArgs args) { - var configuration = args.Configuration.GetOssManagementConfiguration(); await _ossObjectAppService.DeleteAsync(new GetOssObjectInput { - Bucket = configuration.Bucket, + Bucket = args.ContainerName, Path = GetOssPath(args), Object = GetOssName(args), }); @@ -38,10 +37,9 @@ public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency public override async Task ExistsAsync(BlobProviderExistsArgs args) { - var configuration = args.Configuration.GetOssManagementConfiguration(); var result = await _ossObjectAppService.ExistsAsync(new GetOssObjectInput { - Bucket = configuration.Bucket, + Bucket = args.ContainerName, Path = GetOssPath(args), Object = GetOssName(args), }); @@ -50,10 +48,9 @@ public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency public override async Task GetOrNullAsync(BlobProviderGetArgs args) { - var configuration = args.Configuration.GetOssManagementConfiguration(); var content = await _ossObjectAppService.GetAsync(new GetOssObjectInput { - Bucket = configuration.Bucket, + Bucket = args.ContainerName, Path = GetOssPath(args), Object = GetOssName(args), }); @@ -63,10 +60,9 @@ public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency public override async Task SaveAsync(BlobProviderSaveArgs args) { - var configuration = args.Configuration.GetOssManagementConfiguration(); await _ossObjectAppService.CreateAsync(new CreateOssObjectInput { - Bucket = configuration.Bucket, + Bucket = args.ContainerName, Overwrite = args.OverrideExisting, Path = GetOssPath(args), FileName = GetOssName(args), @@ -76,16 +72,14 @@ public class OssManagementBlobProvider : BlobProviderBase, ITransientDependency protected virtual string GetOssPath(BlobProviderArgs args) { - // ContainerName: blob - // path1/path2/path3/path3/path5/file.txt => blob/path1/path2/path3/path3/path5/ - var path = args.ContainerName; + // path1/path2/path3/path3/path5/file.txt => path1/path2/path3/path3/path5/ if (args.BlobName.Contains("/")) { var lastIndex = args.BlobName.LastIndexOf('/'); - path += args.BlobName.Substring(0, lastIndex); + return args.BlobName.Substring(0, lastIndex); } - return path.EnsureEndsWith('/'); + return args.BlobName.EnsureEndsWith('/'); } protected virtual string GetOssName(BlobProviderArgs args) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Minio/LINGYUN/Abp/OssManagement/Minio/MinioOssContainer.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Minio/LINGYUN/Abp/OssManagement/Minio/MinioOssContainer.cs index f2378750f..be388e879 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Minio/LINGYUN/Abp/OssManagement/Minio/MinioOssContainer.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Minio/LINGYUN/Abp/OssManagement/Minio/MinioOssContainer.cs @@ -119,6 +119,7 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor var objectName = objectPath.IsNullOrWhiteSpace() ? request.Object : objectPath + request.Object; + var isDir = false; if (!request.Overwrite && await ObjectExists(client, bucket, objectName)) { @@ -137,12 +138,13 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor } if (request.Content.IsNullOrEmpty()) { + isDir = true; var emptyContent = "This is an OSS object that simulates a directory.".GetBytes(); request.SetContent(new MemoryStream(emptyContent)); } var putResponse = await client.PutObjectAsync(new PutObjectArgs() .WithBucket(bucket) - .WithObject(objectName) + .WithObject(isDir ? $"{objectName}/_dir" : objectName) .WithStreamData(request.Content) .WithObjectSize(request.Content.Length)); @@ -190,13 +192,6 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor public async override Task DeleteObjectAsync(GetOssObjectRequest request) { - if (request.Object.EndsWith('/')) - { - // Minio系统设计并不支持目录的形式 - // 如果是目录的形式,那必定有文件存在,抛出目录不为空即可 - throw new BusinessException(code: OssManagementErrorCodes.ObjectDeleteWithNotEmpty); - } - var client = GetMinioClient(); var bucket = GetBucket(request.Bucket); @@ -207,8 +202,34 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor ? request.Object : objectPath + request.Object; - if (await BucketExists(client, bucket) && - await ObjectExists(client, bucket, objectName)) + if (objectName.EndsWith('/') && await BucketExists(client, bucket)) + { + var objectNames = new List(); + var objects = client.ListObjectsEnumAsync( + new ListObjectsArgs() + .WithBucket(bucket) + .WithPrefix(objectName) + .WithRecursive(true)); + + await foreach (var @object in objects) + { + objectNames.Add(@object.Key); + } + + var errors = await client.RemoveObjectsAsync( + new RemoveObjectsArgs() + .WithBucket(bucket) + .WithObjects(objectNames)); + + foreach (var error in errors) + { + Logger.LogWarning("Batch deletion of objects failed, error details {code}: {message}", error.Code, error.Message); + } + + return; + } + + if (await ObjectExists(client, bucket, objectName)) { var removeObjectArgs = new RemoveObjectArgs() .WithBucket(bucket) @@ -335,6 +356,11 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor await foreach (var item in listObjectResult) { + // 作为目录占位,无需显示 + if (item.Key.EndsWith("_dir")) + { + continue; + } resultObjects.Add(new OssObject( !objectPath.IsNullOrWhiteSpace() ? item.Key.Replace(objectPath, "") @@ -407,7 +433,7 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor var objectPath = GetBlobPath(prefixPath, request.Path); var objectName = objectPath.IsNullOrWhiteSpace() ? request.Object - : objectPath.EnsureEndsWith('/') + request.Object; + : objectPath + request.Object; if (!await ObjectExists(client, bucket, objectName)) { @@ -535,6 +561,6 @@ public class MinioOssContainer : OssContainerBase, IOssObjectExpireor path.IsNullOrWhiteSpace() ? "" : path.Replace("./", "").RemovePreFix("/"))}"; - return resultPath.Replace("//", ""); + return resultPath.Replace("//", "").EnsureEndsWith('/'); } } From 25d7281209253c7a8e846e2011a7d70a4f764255 Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 28 May 2025 11:26:22 +0800 Subject: [PATCH 02/37] fix(localization): Fix the potential stamp cache expiration null reference exception --- .../External/ExternalLocalizationTextStoreCache.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/External/ExternalLocalizationTextStoreCache.cs b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/External/ExternalLocalizationTextStoreCache.cs index 1a216363f..c56e203b2 100644 --- a/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/External/ExternalLocalizationTextStoreCache.cs +++ b/aspnet-core/modules/localization-management/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/External/ExternalLocalizationTextStoreCache.cs @@ -84,14 +84,15 @@ public class ExternalLocalizationTextStoreCache : IExternalLocalizationTextStore var stampCacheKey = ExternalLocalizationTextStampCacheItem.CalculateCacheKey(resource.ResourceName, cultureName); var stampCacheItem = await StampCache.GetAsync(stampCacheKey); - if (memoryCacheItem != null && memoryCacheItem.CacheStamp == stampCacheItem.Stamp) + + if (memoryCacheItem != null && memoryCacheItem.CacheStamp == stampCacheItem?.Stamp) { memoryCacheItem.LastCheckTime = DateTime.Now; return memoryCacheItem.Texts; } var distributeCacheItem = await DistributedCache.GetAsync(cacheKey); - if (distributeCacheItem != null) + if (stampCacheItem != null && distributeCacheItem != null) { MemoryCache[cacheKey] = new LocalizationTextMemoryCacheItem(distributeCacheItem.Texts, stampCacheItem.Stamp); From bf57331f518c272b81d4b9d1ce8504a1424ba132 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 30 May 2025 13:13:56 +0800 Subject: [PATCH 03/37] feat(vben5): Add changing password when login --- .../src/views/_core/authentication/login.vue | 18 ++- .../authentication/should-change-password.vue | 120 ++++++++++++++++++ .../packages/@abp/account/src/types/token.ts | 6 + 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 apps/vben5/apps/app-antd/src/views/_core/authentication/should-change-password.vue diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue index d85f849bd..ea2963eef 100644 --- a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue +++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue @@ -1,5 +1,5 @@ @@ -111,5 +126,6 @@ onMounted(onInit); + diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/should-change-password.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/should-change-password.vue new file mode 100644 index 000000000..e6c010ebf --- /dev/null +++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/should-change-password.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/apps/vben5/packages/@abp/account/src/types/token.ts b/apps/vben5/packages/@abp/account/src/types/token.ts index afa1fc984..b6c796980 100644 --- a/apps/vben5/packages/@abp/account/src/types/token.ts +++ b/apps/vben5/packages/@abp/account/src/types/token.ts @@ -69,6 +69,11 @@ interface TwoFactorError extends OAuthError { userId: string; } +interface ShouldChangePasswordError extends OAuthError { + changePasswordToken: string; + userId: string; +} + export type { OAuthError, OAuthTokenRefreshModel, @@ -76,6 +81,7 @@ export type { PasswordTokenRequest, PasswordTokenRequestModel, QrCodeTokenRequest, + ShouldChangePasswordError, TokenRequest, TokenResult, TwoFactorError, From 713dd9276ca1482ca269bd5f65fede5bcebbd49d Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 30 May 2025 13:22:51 +0800 Subject: [PATCH 04/37] feat(vben5): Increase the localization of certification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 账户锁定 - 账户未启用 - 用户名或密码错误 - Token已过期 - 需要二次认证 - 密码已过期 --- .../app-antd/src/locales/langs/en-US/abp.json | 8 +++++ .../app-antd/src/locales/langs/zh-CN/abp.json | 8 +++++ .../@abp/account/src/hooks/useOAuthError.ts | 34 +++++++++++++++++-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json index d6145c3b6..277f4585d 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json @@ -11,6 +11,14 @@ }, "qrcodeLogin": { "scaned": "Please confirm login on your phone." + }, + "errors": { + "accountLockedByInvalidLoginAttempts": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.", + "accountInactive": "You are not allowed to login! Your account is inactive.", + "invalidUserNameOrPassword": "Invalid username or password!", + "tokenHasExpired": "The token is no longer valid!", + "requiresTwoFactor": "Identity verification is required. Please select a verification method!", + "shouldChangePassword": "Your password has expired. Please change it and login!" } }, "manage": { diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json index 8edd4ab47..0de0f54f4 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json @@ -11,6 +11,14 @@ }, "qrcodeLogin": { "scaned": "请在手机上确认登录." + }, + "errors": { + "accountLockedByInvalidLoginAttempts": "由于尝试登录无效,用户帐户被锁定.请稍候再试!", + "accountInactive": "您不能登录,您的帐户是无效的!", + "invalidUserNameOrPassword": "用户名或密码错误!", + "tokenHasExpired": "您的请求会话已过期,请重新登录!", + "requiresTwoFactor": "需要验证身份,请选择一种验证方式!", + "shouldChangePassword": "您的密码已失效,请修改密码后登录!" } }, "manage": { diff --git a/apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts b/apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts index d40ada311..456f76525 100644 --- a/apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts +++ b/apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts @@ -1,3 +1,5 @@ +import { $t } from '@vben/locales'; + interface OAuthError { error: string; error_description?: string; @@ -6,8 +8,36 @@ interface OAuthError { export function useOAuthError() { function formatError(error: OAuthError) { - // TODO: 解决oauth消息国际化. - return error.error_description; + switch (error.error_description) { + // 用户名或密码无效 + case 'Invalid username or password!': { + return $t('abp.oauth.errors.invalidUserNameOrPassword'); + } + // 需要二次认证 + case 'RequiresTwoFactor': { + return $t('abp.oauth.errors.requiresTwoFactor'); + } + // 需要更改密码 + case 'ShouldChangePasswordOnNextLogin': { + return $t('abp.oauth.errors.shouldChangePassword'); + } + // Token已失效 + case 'The token is no longer valid.': { + return $t('abp.oauth.errors.tokenHasExpired'); + } + // 用户尝试登录次数太多,用户被锁定 + case 'The user account has been locked out due to invalid login attempts. Please wait a while and try again.': { + return $t('abp.oauth.errors.accountLockedByInvalidLoginAttempts'); + } + // 用户未启用 + case 'You are not allowed to login! Your account is inactive.': { + return $t('abp.oauth.errors.accountInactive'); + } + // 其他不常用的错误信息返回原始字符串 + default: { + return error.error_description; + } + } } return { From a9282a22453120193ac991bfee96e98efe30df5c Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 30 May 2025 14:17:50 +0800 Subject: [PATCH 05/37] feat(vben5): Realize password recovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完善登录页忘记密码 --- .../_core/authentication/forget-password.vue | 143 ++++++++++++++++-- 1 file changed, 132 insertions(+), 11 deletions(-) diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue index fef0d4279..cf23f56b2 100644 --- a/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue +++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue @@ -1,41 +1,162 @@