diff --git a/docs/en/Blob-Storing-Aliyun.md b/docs/en/Blob-Storing-Aliyun.md index 6621e2dbd2..602d666787 100644 --- a/docs/en/Blob-Storing-Aliyun.md +++ b/docs/en/Blob-Storing-Aliyun.md @@ -45,17 +45,19 @@ Configure(options => * **AccessKeyId** ([NotNull]string): AccessKey is the key to access the Alibaba Cloud API. It has full permissions for the account. Please keep it safe! Recommend to follow [Alibaba Cloud security best practicess](https://help.aliyun.com/document_detail/102600.html),Use RAM sub-user AccessKey to call API. * **AccessKeySecret** ([NotNull]string): Same as above. -* **Endpoint** ([NotNull]string): Endpoint is the external domain name of OSS. See the [document](https://help.aliyun.com/document_detail/31837.html) for details. +* **Endpoint** ([NotNull]string): Endpoint is the external domain name of OSS. See the [document](https://help.aliyun.com/document_detail/31837.html) for details. +* **UseSecurityTokenService** (bool): Use [STS temporary credentials](https://help.aliyun.com/document_detail/100624.html) to access OSS services,default: `false`. * **RegionId** (string): Access address of STS service. See the [document](https://help.aliyun.com/document_detail/66053.html) for details. * **RoleArn** ([NotNull]string): STS required role ARN. See the [document](https://help.aliyun.com/document_detail/100624.html) for details. * **RoleSessionName** ([NotNull]string): Used to identify the temporary access credentials, it is recommended to use different application users to distinguish. * **Policy** (string): Additional permission restrictions. See the [document](https://help.aliyun.com/document_detail/100680.html) for details. -* **DurationSeconds** (int): Validity period(s) of a temporary access certificate,minimum is 900 and the maximum is 3600. **note**: Using subaccounts operated OSS,if the value is 0. +* **DurationSeconds** (int): Validity period(s) of a temporary access certificate,minimum is 900 and the maximum is 3600. * **ContainerName** (string): You can specify the container name in Aliyun. If this is not specified, it uses the name of the BLOB container defined with the `BlogContainerName` attribute (see the [BLOB storing document](Blob-Storing.md)). Please note that Aliyun has some **rules for naming containers**. A container name must be a valid DNS name, conforming to the [following naming rules](https://help.aliyun.com/knowledge_detail/39668.html): * Container names must start or end with a letter or number, and can contain only letters, numbers, and the dash (-) character. * Container names Must start and end with lowercase letters and numbers. * Container names must be from **3** through **63** characters long. * **CreateContainerIfNotExists** (bool): Default value is `false`, If a container does not exist in Aliyun, `AliyunBlobProvider` will try to create it. +* **TemporaryCredentialsCacheKey** (bool): The cache key of STS credentials. ## Aliyun Blob Name Calculator @@ -70,4 +72,4 @@ Aliyun Blob Provider organizes BLOB name and implements some conventions. The fu * `AliyunBlobProvider` is the main service that implements the Aliyun BLOB storage provider, if you want to override/replace it via [dependency injection](Dependency-Injection.md) (don't replace `IBlobProvider` interface, but replace `AliyunBlobProvider` class). * `IAliyunBlobNameCalculator` is used to calculate the full BLOB name (that is explained above). It is implemented by the `DefaultAliyunBlobNameCalculator` by default. -* `IOssClientFactory` is used create OSS client. It is implemented by the `DefaultOssClientFactory` by default. You can override/replace it,if you want customize. \ No newline at end of file +* `IOssClientFactory` is used create OSS client. It is implemented by the `DefaultOssClientFactory` by default. You can override/replace it,if you want customize. diff --git a/docs/zh-Hans/Blob-Storing-Aliyun.md b/docs/zh-Hans/Blob-Storing-Aliyun.md index adeb902830..ba66d55104 100644 --- a/docs/zh-Hans/Blob-Storing-Aliyun.md +++ b/docs/zh-Hans/Blob-Storing-Aliyun.md @@ -46,16 +46,18 @@ Configure(options => * **AccessKeyId** ([NotNull]string): 云账号AccessKey是访问阿里云API的密钥,具有该账户完全的权限,请你务必妥善保管!强烈建议遵循[阿里云安全最佳实践](https://help.aliyun.com/document_detail/102600.html),使用RAM子用户AccessKey来进行API调用. * **AccessKeySecret** ([NotNull]string): 同上. * **Endpoint** ([NotNull]string): Endpoint表示OSS对外服务的访问域名. [访问域名和数据中心](https://help.aliyun.com/document_detail/31837.html) +* **UseSecurityTokenService** (bool): 是否使用STS临时授权访问OSS,默认false. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) * **RegionId** (string): STS服务的接入地址,每个地址的功能都相同,请尽量在同地域进行调用. [接入地址](https://help.aliyun.com/document_detail/66053.html) -* **RoleArn** ([NotNull]string): STS所需角色ARN. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) +* **RoleArn** ([NotNull]string): STS所需角色ARN. * **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分. * **Policy** (string): 在扮演角色的时候额外添加的权限限制. 请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html). -* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. **注**:为0则使用子账号操作OSS. +* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. * **ContainerName** (string): 你可以在aliyun中指定容器名称. 如果没有指定它将使用 `BlogContainerName` 属性定义的BLOB容器的名称(请参阅[BLOB存储文档](Blob-Storing.md)). 请注意Aliyun有一些**命名容器的规则**,容器名称必须是有效的DNS名称,[符合以下命名规则](https://help.aliyun.com/knowledge_detail/39668.html): * 只能包含小写字母,数字和短横线(-) * 必须以小写字母和数字开头和结尾 * Bucket名称的长度限制在**3**到**63**个字符之间 * **CreateContainerIfNotExists** (bool): 默认值为 `false`, 如果aliyun中不存在容器, `AliyunBlobProvider` 将尝试创建它. +* **TemporaryCredentialsCacheKey** (bool): STS凭证缓存Key,默认Guid.NewGuid().ToString("N"). ## Aliyun BLOB 名称计算器 diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs index 058f65412c..757ad4b54c 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs @@ -25,6 +25,12 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value))); } + public bool UseSecurityTokenService + { + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.UseSecurityTokenService, false); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseSecurityTokenService, value); + } + public string RegionId { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RegionId); @@ -88,18 +94,19 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateContainerIfNotExists, value); } - private readonly BlobContainerConfiguration _containerConfiguration; - - public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + private readonly string _temporaryCredentialsCacheKey; + public string TemporaryCredentialsCacheKey { - _containerConfiguration = containerConfiguration; + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.TemporaryCredentialsCacheKey, _temporaryCredentialsCacheKey); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.TemporaryCredentialsCacheKey, value); } + private readonly BlobContainerConfiguration _containerConfiguration; - public string ToKeyString() + public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) { - Uri uPoint = new Uri(Endpoint); - return $"blobstoring:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()},rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; + _containerConfiguration = containerConfiguration; + _temporaryCredentialsCacheKey = Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs index c889b374cc..0f8585f12b 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs @@ -5,12 +5,14 @@ public const string AccessKeyId = "Aliyun.AccessKeyId"; public const string AccessKeySecret = "Aliyun.AccessKeySecret"; public const string Endpoint = "Aliyun.Endpoint"; + public const string UseSecurityTokenService = "Aliyun.UseSecurityTokenService"; public const string RegionId = "Aliyun.RegionId"; public const string RoleArn = "Aliyun.RoleArn"; public const string RoleSessionName = "Aliyun.RoleSessionName"; public const string DurationSeconds = "Aliyun.DurationSeconds"; public const string Policy = "Aliyun.Policy"; - public const string ContainerName = "Aliyun:ContainerName"; + public const string ContainerName = "Aliyun.ContainerName"; public const string CreateContainerIfNotExists = "Aliyun.CreateContainerIfNotExists"; + public const string TemporaryCredentialsCacheKey = "Aliyun.TemporaryCredentialsCacheKey"; } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs similarity index 66% rename from framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs rename to framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs index e0d75f2954..508d94b743 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunTemporaryCredentialsCacheItem.cs @@ -6,8 +6,7 @@ using Volo.Abp.Caching; namespace Volo.Abp.BlobStoring.Aliyun { [Serializable] - [CacheName("AssumeRoleCredentials")] - public class AssumeRoleCredentialsCacheItem + public class AliyunTemporaryCredentialsCacheItem { public string AccessKeyId { get; set; } @@ -15,12 +14,12 @@ namespace Volo.Abp.BlobStoring.Aliyun public string SecurityToken { get; set; } - public AssumeRoleCredentialsCacheItem() + public AliyunTemporaryCredentialsCacheItem() { } - public AssumeRoleCredentialsCacheItem(string accessKeyId,string accessKeySecret,string securityToken) + public AliyunTemporaryCredentialsCacheItem(string accessKeyId,string accessKeySecret,string securityToken) { AccessKeyId = accessKeyId; AccessKeySecret = accessKeySecret; diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index 8f69603f7c..ea0c4cbf28 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -6,8 +6,11 @@ using Aliyun.OSS; using Microsoft.Extensions.Caching.Distributed; using System; using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Encryption; +using static Aliyun.Acs.Core.Auth.Sts.AssumeRoleResponse; namespace Volo.Abp.BlobStoring.Aliyun { @@ -16,53 +19,82 @@ namespace Volo.Abp.BlobStoring.Aliyun /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { - protected IDistributedCache Cache { get; } + protected IDistributedCache Cache { get; } + + protected IStringEncryptionService StringEncryptionService { get; } + public DefaultOssClientFactory( - IDistributedCache cache) + IDistributedCache cache, + IStringEncryptionService stringEncryptionService) { Cache = cache; + StringEncryptionService = stringEncryptionService; } - public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) + public virtual IOss Create(AliyunBlobProviderConfiguration configuration) { - //Sub-account - if (aliyunConfig.DurationSeconds <= 0) + Check.NotNullOrWhiteSpace(configuration.AccessKeyId, nameof(configuration.AccessKeyId)); + Check.NotNullOrWhiteSpace(configuration.AccessKeySecret, nameof(configuration.AccessKeySecret)); + Check.NotNullOrWhiteSpace(configuration.Endpoint, nameof(configuration.Endpoint)); + if (configuration.UseSecurityTokenService) { - return new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); + //STS temporary authorization to access OSS + return GetSecurityTokenClient(configuration); } - else + //Sub-account + return new OssClient(configuration.Endpoint, configuration.AccessKeyId, configuration.AccessKeySecret); + } + + protected virtual IOss GetSecurityTokenClient(AliyunBlobProviderConfiguration configuration) + { + Check.NotNullOrWhiteSpace(configuration.RoleArn, nameof(configuration.RoleArn)); + Check.NotNullOrWhiteSpace(configuration.RoleSessionName, nameof(configuration.RoleSessionName)); + var cacheItem = Cache.Get(configuration.TemporaryCredentialsCacheKey); + if (cacheItem == null) { - //STS temporary authorization to access OSS - var key = aliyunConfig.ToKeyString(); - var cacheItem = Cache.Get(key); - if (cacheItem == null) + IClientProfile profile = DefaultProfile.GetProfile( + configuration.RegionId, + configuration.AccessKeyId, + configuration.AccessKeySecret); + DefaultAcsClient client = new DefaultAcsClient(profile); + AssumeRoleRequest request = new AssumeRoleRequest { - IClientProfile profile = DefaultProfile.GetProfile( - aliyunConfig.RegionId, - aliyunConfig.AccessKeyId, - aliyunConfig.AccessKeySecret); - DefaultAcsClient client = new DefaultAcsClient(profile); - AssumeRoleRequest request = new AssumeRoleRequest - { - AcceptFormat = FormatType.JSON, - //eg:acs:ram::$accountID:role/$roleName - RoleArn = aliyunConfig.RoleArn, - RoleSessionName = aliyunConfig.RoleSessionName, - //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 - DurationSeconds = aliyunConfig.DurationSeconds, - //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy - Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, - }; - var response = client.GetAcsResponse(request); - cacheItem = new AssumeRoleCredentialsCacheItem(response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); - Cache.Set(key, cacheItem, new DistributedCacheEntryOptions() - { - //Subtract 10 seconds of network request time. - AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(aliyunConfig.DurationSeconds - 10) - }); - } - return new OssClient(aliyunConfig.Endpoint, cacheItem.AccessKeyId, cacheItem.AccessKeySecret, cacheItem.SecurityToken); + AcceptFormat = FormatType.JSON, + //eg:acs:ram::$accountID:role/$roleName + RoleArn = configuration.RoleArn, + RoleSessionName = configuration.RoleSessionName, + //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 + DurationSeconds = configuration.DurationSeconds, + //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy + Policy = configuration.Policy.IsNullOrEmpty() ? null : configuration.Policy, + }; + var response = client.GetAcsResponse(request); + cacheItem = SetTemporaryCredentialsCache(configuration, response.Credentials); } + return new OssClient( + configuration.Endpoint, + StringEncryptionService.Decrypt(cacheItem.AccessKeyId), + StringEncryptionService.Decrypt(cacheItem.AccessKeySecret), + StringEncryptionService.Decrypt(cacheItem.SecurityToken)); + } + + private AliyunTemporaryCredentialsCacheItem SetTemporaryCredentialsCache( + AliyunBlobProviderConfiguration configuration, + AssumeRole_Credentials credentials) + { + var temporaryCredentialsCache = new AliyunTemporaryCredentialsCacheItem( + StringEncryptionService.Encrypt(credentials.AccessKeyId), + StringEncryptionService.Encrypt(credentials.AccessKeySecret), + StringEncryptionService.Encrypt(credentials.SecurityToken)); + + Cache.Set(configuration.TemporaryCredentialsCacheKey, temporaryCredentialsCache, + new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(configuration.DurationSeconds - 10) + }); + + return temporaryCredentialsCache; } + } } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs index 1635e3390f..b08737eba5 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -50,14 +50,16 @@ namespace Volo.Abp.BlobStoring.Aliyun aliyun.AccessKeySecret = accessKeySecret; aliyun.Endpoint = endpoint; //STS + aliyun.UseSecurityTokenService = true; aliyun.RegionId = regionId; aliyun.RoleArn = roleArn; aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); - aliyun.DurationSeconds = 0; + aliyun.DurationSeconds = 900; aliyun.Policy = String.Empty; //Other aliyun.CreateContainerIfNotExists = true; aliyun.ContainerName = _randomContainerName; + aliyun.TemporaryCredentialsCacheKey = "297A96094D7048DBB2C28C3FDB20839A"; _configuration = aliyun; }); });