如何高效从共享Azure Key Vault下载指定密钥?含代码优化需求
我们的平台使用一个共享Azure Key Vault实例,存储了5000+供各类应用使用的密钥。我需要高效获取仅与我负责的特定应用相关的约10个密钥,但当前方案需先下载所有密钥属性再本地过滤,导致大量服务器往返请求,在密钥数量庞大时存在性能瓶颈。
当前实现代码如下:
// 获取Key Vault中所有密钥的名称 public async IAsyncEnumerable<string> GetKeys() { var keyVaultSecureValueManager = (KeyVaultSecureValueManager)this.SecureValueManager; var allPages = keyVaultSecureValueManager.Client.GetPropertiesOfSecretsAsync().AsPages(); await foreach (var page in allPages) { foreach (var secretProperties in page.Values) { yield return secretProperties.Name; } } } // 本地过滤出目标密钥 private static async Task<List<string>> GetKeysAsync(IAsyncEnumerable<string> keyvaultKeys) { List<string> keys = new List<string>(); await foreach (var key in keyvaultKeys) { if (key.StartsWith("myKeys", StringComparison.InvariantCultureIgnoreCase)) { keys.Add(key); } } return keys; } // 获取单个密钥值 private async Task<KeyVaultResponse> GetCertAsync(string key) { return await secretManager.GetKeyAsync(key); } // 获取所有目标证书密钥 public async Task<Certs> GetAllCertsAsync() { var keyvaultKeys = this._secretManager.GetKeys(); var selectedKeys = await GetKeysAsync(keyvaultKeys); var allCertKeys = selectedKeys.Where(s => s.EndsWith("cert", StringComparison.InvariantCultureIgnoreCase)); var allPwdKeys = selectedKeys.Where(s => s.EndsWith("pwd", StringComparison.InvariantCultureIgnoreCase)); // 并发获取所有证书 var fetchTasks = allCertKeys.Select(c => this.GetCertAsync(c, allPwdKeys)); var results = await Task.WhenAll(fetchTasks); // 检查请求错误 var badRequest = results.FirstOrDefault(res => res.IsBadRequest); if (badRequest != null) { return BadRequestResponse(badRequest.ErrorMessage); } return results; }
我有两个技术问题需要解决:
- 使用
Azure.Security.KeyVault.Secrets命名空间下的SecretClient时,能否通过服务器端过滤减少服务器往返次数? - 共享Azure Key Vault中组织密钥的推荐最佳实践有哪些?例如按应用或领域分组以实现特定集合查询。
1. 服务器端过滤减少往返次数
可以通过SecretClient.GetPropertiesOfSecretsAsync的**Filter参数**实现服务器端过滤,利用OData筛选器直接在Azure Key Vault服务端筛选密钥,无需拉取全量数据,大幅减少服务器往返次数和数据传输量。
代码示例:按名称前缀服务器端筛选
修改原GetKeys方法,添加OData筛选器筛选名称以myKeys开头的密钥:
using Azure.Security.KeyVault.Secrets; public async IAsyncEnumerable<string> GetFilteredKeys() { var keyVaultSecureValueManager = (KeyVaultSecureValueManager)this.SecureValueManager; SecretClient client = keyVaultSecureValueManager.Client; // 服务器端筛选:名称以"myKeys"开头(OData filter语法) var filter = $"name starts with 'myKeys'"; var filteredPages = client.GetPropertiesOfSecretsAsync(filter: filter).AsPages(); await foreach (var page in filteredPages) { foreach (var secretProperties in page.Values) { yield return secretProperties.Name; } } }
进阶:按标签筛选
如果密钥带有标签(比如app=myapp),也可以通过标签做服务器端筛选:
// 筛选标签app等于myapp的密钥 var filter = $"tags/app eq 'myapp'"; var filteredPages = client.GetPropertiesOfSecretsAsync(filter: filter).AsPages();
直接批量获取已知密钥
如果已经明确知道目标密钥的名称,无需先查询属性,直接批量并发获取密钥值,进一步优化性能:
public async Task<Certs> GetAllCertsDirectlyAsync() { // 预先定义目标密钥名称列表 var targetCertKeys = new List<string> { "myKeys-db-cert", "myKeys-api-cert" }; var targetPwdKeys = new List<string> { "myKeys-db-pwd", "myKeys-api-pwd" }; // 并发获取所有证书密钥 var fetchTasks = targetCertKeys.Select(c => this.GetCertAsync(c)); var results = await Task.WhenAll(fetchTasks); // 检查请求错误 var badRequest = results.FirstOrDefault(res => res.IsBadRequest); if (badRequest != null) { return BadRequestResponse(badRequest.ErrorMessage); } return results; }
2. 共享Azure Key Vault密钥组织最佳实践
- 统一命名规范
采用分层命名结构,包含应用标识、环境、密钥类型等信息,例如:{应用名}-{环境}-{资源类型}-{密钥用途}
比如:myapp-dev-db-cert、myapp-prod-api-pwd
这种命名方式便于通过OData前缀筛选,也能快速识别密钥归属。
- 利用标签(Tags)分类
给每个密钥添加标签,比如app=myapp、env=dev、type=cert,支持更灵活的服务器端筛选,同时便于在Azure门户中快速分组查看。
- 最小权限访问控制(Azure RBAC)
避免给应用分配Key Vault的全局权限,通过Azure RBAC的密钥/证书/机密权限,结合Filter条件限制应用只能访问特定前缀或标签的密钥。例如创建自定义RBAC角色,允许应用仅读取名称以myapp-开头的密钥。
- 避免全量拉取密钥属性
永远优先使用服务器端过滤(OData筛选器),不要拉取所有密钥后再本地过滤,尤其当Key Vault中密钥数量超过1000个时,全量拉取会触发多次分页请求,严重影响性能。
- 缓存密钥(按需)
对于不频繁变更的密钥,可在AKS应用中添加本地缓存(比如用IMemoryCache或分布式缓存),减少对Key Vault的重复请求,进一步提升性能。注意缓存过期时间要匹配密钥的更新频率,避免使用过期密钥。
内容的提问来源于stack exchange,提问作者pingpong2020

