Azure Key Vault 机密 Java SDK

azure-security-keyvault-secrets-java
分类编程
作者Agentic Awesome Skills 社区
许可MIT
评分4.90/5
使用12.4K

Azure Key Vault Secrets (Java)

安全地存储和管理密码、API 密钥和连接字符串等机密。

安装

xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-security-keyvault-secrets</artifactId>
    <version>4.9.0</version>
</dependency>

创建客户端

java
import com.azure.security.keyvault.secrets.SecretClient;
import com.azure.security.keyvault.secrets.SecretClientBuilder;
import com.azure.identity.DefaultAzureCredentialBuilder;

// 同步客户端
SecretClient secretClient = new SecretClientBuilder()
.vaultUrl("https://<vault-name>.vault.azure.net")
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();

// 异步客户端
SecretAsyncClient secretAsyncClient = new SecretClientBuilder()
.vaultUrl("https://<vault-name>.vault.azure.net")
.credential(new DefaultAzureCredentialBuilder().build())
.buildAsyncClient();

创建/设置机密

java
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;

// 简单机密
KeyVaultSecret secret = secretClient.setSecret("database-password", "P@ssw0rd123!");
System.out.println("Secret name: " + secret.getName());
System.out.println("Secret ID: " + secret.getId());

// 带选项的机密
KeyVaultSecret secretWithOptions = secretClient.setSecret(
new KeyVaultSecret("api-key", "sk_live_abc123xyz")
.setProperties(new SecretProperties()
.setContentType("application/json")
.setExpiresOn(OffsetDateTime.now().plusYears(1))
.setNotBefore(OffsetDateTime.now())
.setEnabled(true)
.setTags(Map.of(
"environment", "production",
"service", "payment-api"
))
)
);

获取机密

java
// 获取最新版本
KeyVaultSecret secret = secretClient.getSecret("database-password");
String value = secret.getValue();
System.out.println("Secret value: " + value);

// 获取特定版本
KeyVaultSecret specificVersion = secretClient.getSecret("database-password", "<version-id>");

// 仅获取属性(不含值)
SecretProperties props = secretClient.getSecret("database-password").getProperties();
System.out.println("Enabled: " + props.isEnabled());
System.out.println("Created: " + props.getCreatedOn());

更新机密属性

java
// 获取机密
KeyVaultSecret secret = secretClient.getSecret("api-key");

// 更新属性(不能更新值 - 请创建新版本)
secret.getProperties()
.setEnabled(false)
.setExpiresOn(OffsetDateTime.now().plusMonths(6))
.setTags(Map.of("status", "rotating"));

SecretProperties updated = secretClient.updateSecretProperties(secret.getProperties());
System.out.println("Updated: " + updated.getUpdatedOn());

列出机密

java
import com.azure.core.util.paging.PagedIterable;
import com.azure.security.keyvault.secrets.models.SecretProperties;

// 列出所有机密(仅属性,不含值)
for (SecretProperties secretProps : secretClient.listPropertiesOfSecrets()) {
System.out.println("Secret: " + secretProps.getName());
System.out.println(" Enabled: " + secretProps.isEnabled());
System.out


.println(" Created: " + secretProps.getCreatedOn());
System.out.println(" Content-Type: " + secretProps.getContentType());

// 如有需要,获取其值
if (secretProps.isEnabled()) {
KeyVaultSecret fullSecret = secretClient.getSecret(secretProps.getName());
System.out.println(" Value: " + fullSecret.getValue().substring(0, 5) + "...");
}
}

// 列出密钥的版本
for (SecretProperties version : secretClient.listPropertiesOfSecretVersions("database-password")) {
System.out.println("Version: " + version.getVersion());
System.out.println("Created: " + version.getCreatedOn());
System.out.println("Enabled: " + version.isEnabled());
}

code
## 删除密钥
java
import com.azure.core.util.polling.SyncPoller;
import com.azure.security.keyvault.secrets.models.DeletedSecret;

// 开始删除(对于启用软删除的存储库,将返回 poller)
SyncPoller<DeletedSecret, Void> deletePoller = secretClient.beginDeleteSecret("old-secret");

// 等待删除完成
DeletedSecret deletedSecret = deletePoller.poll().getValue();
System.out.println("Deleted on: " + deletedSecret.getDeletedOn());
System.out.println("Scheduled purge: " + deletedSecret.getScheduledPurgeDate());

deletePoller.waitForCompletion();

code
## 恢复已删除的密钥
java
// 列出已删除的密钥
for (DeletedSecret deleted : secretClient.listDeletedSecrets()) {
System.out.println("Deleted: " + deleted.getName());
System.out.println("Deletion date: " + deleted.getDeletedOn());
}

// 恢复已删除的密钥
SyncPoller<KeyVaultSecret, Void> recoverPoller = secretClient.beginRecoverDeletedSecret("old-secret");
recoverPoller.waitForCompletion();

KeyVaultSecret recovered = recoverPoller.getFinalResult();
System.out.println("Recovered: " + recovered.getName());

code
## 清除已删除的密钥
java
// 永久删除(无法恢复)
secretClient.purgeDeletedSecret("old-secret");

// 先获取已删除密钥的信息
DeletedSecret deleted = secretClient.getDeletedSecret("old-secret");
System.out.println("Will purge: " + deleted.getName());
secretClient.purgeDeletedSecret("old-secret");

code
## 备份与还原
java
// 备份密钥(包含所有版本)
byte[] backup = secretClient.backupSecret("important-secret");

// 保存到文件
Files.write(Paths.get("secret-backup.blob"), backup);

// 从备份还原
byte[] backupData = Files.readAllBytes(Paths.get("secret-backup.blob"));
KeyVaultSecret restored = secretClient.restoreSecretBackup(backupData);
System.out.println("Restored: " + restored.getName());

code
## 异步操作
java
SecretAsyncClient asyncClient = new SecretClientBuilder()
.vaultUrl("https://<vault>.vault.azure.net")
.credential(new DefaultAzureCredentialBuilder().build())
.buildAsyncClient();

// 异步设置密钥
asyncClient.setSecret("async-secret", "async-value")
.subscribe(
secret -> System.out.println("Created: " + secret.getName()),
error -> System.out.println("Error: " + error.getMessage())
);

// 异步获取密钥
asyncClient.getSecret("async-secret")
.subscribe(secret -> System.out.println("Value: " + secret.getValue()));

// 异步列出密钥
asyncClient.listPropertiesOfSecrets()
.doOnNext(props -> System.out.println("Found: " + props.getName()))
.subscribe();

code
## 配置模式

加载多个密钥

java public class ConfigLoader { private final SecretClient client; public ConfigLoader(String vaultUrl) { this
code
.client = new SecretClientBuilder()
            .vaultUrl(vaultUrl)
            .credential(new DefaultAzureCredentialBuilder().build())
            .buildClient();
    }
    
    public Map<String, String> loadSecrets(List<String> secretNames) {
        Map<String, String> secrets = new HashMap<>();
        for (String name : secretNames) {
            try {
                KeyVaultSecret secret = client.getSecret(name);
                secrets.put(name, secret.getValue());
            } catch (ResourceNotFoundException e) {
                System.out.println("Secret not found: " + name);
            }
        }
        return secrets;
    }
}

// 使用示例
ConfigLoader loader = new ConfigLoader("https://my-vault.vault.azure.net");
Map<String, String> config = loader.loadSecrets(
Arrays.asList("db-connection-string", "api-key", "jwt-secret")
);

机密轮转模式 (Secret Rotation Pattern)

java
public void rotateSecret(String secretName, String newValue) {
    // 获取当前机密
    KeyVaultSecret current = secretClient.getSecret(secretName);
    
    // 禁用旧版本
    current.getProperties().setEnabled(false);
    secretClient.updateSecretProperties(current.getProperties());
    
    // 使用新值创建新版本
    KeyVaultSecret newSecret = secretClient.setSecret(secretName, newValue);
    System.out.println("已轮转至版本: " + newSecret.getProperties().getVersion());
}

错误处理

java
import com.azure.core.exception.HttpResponseException;
import com.azure.core.exception.ResourceNotFoundException;

try {
KeyVaultSecret secret = secretClient.getSecret("my-secret");
System.out.println("Value: " + secret.getValue());
} catch (ResourceNotFoundException e) {
System.out.println("未找到机密");
} catch (HttpResponseException e) {
int status = e.getResponse().getStatusCode();
if (status == 403) {
System.out.println("访问被拒绝 - 请检查权限");
} else if (status == 429) {
System.out.println("触发频率限制 - 请稍后重试");
} else {
System.out.println("HTTP 错误: " + status);
}
}

机密属性

| 属性 | 描述 |
|----------|-------------|
| name | 机密名称 |
| value | 机密值 (字符串) |
| id | 完整标识符 URL |
| contentType | MIME 类型提示 |
| enabled | 机密是否可被检索 |
| notBefore | 生效时间 |
| expiresOn | 过期时间 |
| createdOn | 创建时间戳 |
| updatedOn | 最后更新时间戳 |
| recoveryLevel | 软删除恢复级别 |
| tags | 用户定义元数据 |

环境变量

bash
AZURE_KEYVAULT_URL=https://<vault-name>.vault.azure.net

最佳实践

1. 启用软删除 (Soft Delete) - 防止意外删除
2. 使用标签 (Tags) - 为机密标记环境、服务和所有者
3. 设置过期时间 - 对需要轮转的凭据使用 setExpiresOn()
4. 内容类型 (Content Type) - 设置 contentType 以指示格式(例如 application/json
5. 版本管理 - 在轮转期间不要立即删除旧版本
6. 访问日志 - 在 Key Vault 上启用诊断日志
7. 最小权限原则 - 为不同环境使用独立的 Vault

常见机密类型

java
// 数据库连接字符串
secretClient.setSecret(new KeyVaultSecret("db-connection", 
    "Server=myserver.database.windows.net;Database=mydb;...")
    .setProperties(new SecretProperties()
        .setContentType("text/plain")
        .setTa
gs(Map.of("type", "connection-string"))));

// API 密钥
secretClient.setSecret(new KeyVaultSecret("stripe-api-key", "sk_live_...")
.setProperties(new SecretProperties()
.setContentType("text/plain")
.setExpiresOn(OffsetDateTime.now().plusYears(1))));

// JSON 配置
secretClient.setSecret(new KeyVaultSecret("app-config",
"{\"endpoint\":\"https://...\",\"key\":\"...\"}")
.setProperties(new SecretProperties()
.setContentType("application/json")));

// 证书密码
secretClient.setSecret(new KeyVaultSecret("cert-password", "CertP@ss!")
.setProperties(new SecretProperties()
.setContentType("text/plain")
.setTags(Map.of("certificate", "my-cert"))));
```

触发词

  • "Key Vault secrets Java", "secret management Java"
  • "store password", "store API key", "connection string"
  • "retrieve secret", "rotate secret"
  • "Azure secrets", "vault secrets"

使用场景

本技能适用于执行概览中所描述的工作流或操作。

局限性

  • 仅在任务明确符合上述范围时使用此技能。
  • 不要将输出结果视为针对特定环境的验证、测试或专家评审的替代方案。
  • 如果缺少必要的输入、权限、安全边界或成功标准,请停止操作并请求澄清。