

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 AWS Secrets Manager 管理凭证
<a name="manage-credentials-using-aws-secrets-manager"></a>

*Durga Prasad Cheepuri，Amazon Web Services*

## Summary
<a name="manage-credentials-using-aws-secrets-manager-summary"></a>

此模式将引导您使用 AWS Secrets Manager 动态获取 Java Spring 应用程序的数据库凭证。

过去，当您创建自定义应用程序以从数据库中检索信息时，通常必须在应用程序中直接嵌入访问数据库所需凭证（密钥）。当需要轮换凭证时，您必须投入时间更新应用程序以使用新的凭证，然后分配更新后的应用程序。如果您有多个应用程序共享凭证，而您错过更新其中一个，则该应用程序将会失效。为应对此类风险，许多用户选择不定期轮换凭证，然而，这种行为实际上会带来新的风险。

Secrets Manager 允许您将代码中的硬编码凭证（包括密码）替换为 API 调用，以编程方式检索密钥。这有助于确保检查者不会泄露密钥，因为代码中根本不包含密钥。此外，您还可以配置 Secrets Manager 根据您指定的计划自动轮换密钥。这样，您就可以将长期密钥替换为短期密钥，从而显著降低泄露风险。有关更多信息，请参阅[ AWS Secrets Manager 文档](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html)。

## 先决条件和限制
<a name="manage-credentials-using-aws-secrets-manager-prereqs"></a>

**先决条件**
+ 可访问 Secrets Manager 的 Amazon Web Services 账户
+ Java Spring 应用程序

## 架构
<a name="manage-credentials-using-aws-secrets-manager-architecture"></a>

**源技术堆栈**
+ Java Spring 应用程序，其中包含访问数据库的代码，其数据库凭证由 application.properties 文件管理。

**目标技术堆栈**
+ Java Spring 应用程序，其中包含访问数据库的代码，其数据库凭证在 Secrets Manager 中管理。application.properties 文件负责保存 Secrets Manager 的密钥。

**Secrets Manager 与应用程序集成**

![](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/44d359f5-47d9-4228-ac14-a64b5dfa7972/images/fc4b44fd-d1bd-4564-9bc1-c42c896e305b.png)


## 工具
<a name="manage-credentials-using-aws-secrets-manager-tools"></a>
+ **Secrets Manager** - [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 是一项 Amazon Web Service，可让您更轻松地管理密钥。密钥可以是数据库凭证、密码、第三方 API 密钥，甚至是任意文本。你可以使用 Secrets Manager 控制台、Secrets Manager 命令行界面 (CLI) 或 Secrets Manager API 和，集中存储和控制对这些密钥的访问。 SDKs

## 操作说明
<a name="manage-credentials-using-aws-secrets-manager-epics"></a>

### 在 Secrets Manager 中存储密钥
<a name="store-secret-in-secrets-manager"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 将数据库凭证作为密钥存储在 Secrets Manager 中。 | 按照 Secrets Manager 文档中[创建密钥](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html)中的步骤，将 Amazon Relational Database Service (Amazon RDS) 或其他数据库凭证作为密钥存储在 Secrets Manager 中。 | 系统管理员 | 
| 为 Spring 应用程序设置访问 Secrets Manager 的权限。 | 根据 Java Spring 应用程序使用 Secrets Manager 的方式设置相应权限。要控制对密钥的访问权限，请根据 Secrets Manager 文档中[对 Secrets Manager 使用基于身份的策略 (IAM Policy) 和 ABAC ](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_identity-based-policies.html)以及[对 Secrets Manager 使用基于资源的策略](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html)部分提供的信息创建策略。按照 Secrets Manager 文档中[检索密钥值](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_retrieve-secret.html)部分所述的步骤进行操作。 | 系统管理员 | 

### 更新 Spring 应用程序
<a name="update-the-spring-application"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 添加 JAR 依赖项以使用 Secrets Manager。 | 有关详细信息，请参阅*其他信息*部分。 | Java 开发人员 | 
| 将密钥的详细信息添加到 Spring 应用程序。 | 使用密钥名称、端点和 AWS 区域更新 application.properties 文件。有关示例，请参阅*其他信息*部分。 | Java 开发人员 | 
| 在 Java 中更新数据库凭证检索代码。 | 在应用程序中，更新获取数据库凭证的 Java 代码，以从 Secrets Manager 获取这些详细信息。有关示例代码，请参阅*其他信息*部分。 | Java 开发人员 | 

## 相关资源
<a name="manage-credentials-using-aws-secrets-manager-resources"></a>
+ [AWS Secrets Manager 文档](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html)
+ [对 Secrets Manager 使用基于身份的策略 (IAM Policy) 和 ABAC](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_identity-based-policies.html)
+ [对 Secrets Manager 使用基于资源的策略](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html)
+ [示例代码](https://github.com/durgachamz/Spring-secrets-manager) 

## 附加信息
<a name="manage-credentials-using-aws-secrets-manager-additional"></a>

**添加使用 Secrets Manager 所需 JAR 依赖项**

*Maven:*

```
<groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-secretsmanager</artifactId>
    <version>1.11. 355 </version>
```

*Gradle:*

```
compile group: 'com.amazonaws', name: 'aws-java-sdk-secretsmanager', version: '1.11.355'
```

**使用密钥的详细信息更新 application.properties 文件**

```
spring.aws.secretsmanager.secretName=postgres-local
spring.aws.secretsmanager.endpoint=secretsmanager.us-east-1.amazonaws.com
spring.aws.secretsmanager.region=us-east-1
```

**在 Java 中更新数据库凭证检索代码**

```
String  secretName  =  env.getProperty("spring.aws.secretsmanager.secretName");
String  endpoints  =  env.getProperty("spring.aws.secretsmanager.endpoint");
String  AWS Region  =  env.getProperty("spring.aws.secretsmanager.region");
AwsClientBuilder.EndpointConfiguration  config  =  new  AwsClientBuilder.EndpointConfiguration(endpoints, AWS Region);
AWSSecretsManagerClientBuilder  clientBuilder  =  AWSSecretsManagerClientBuilder.standard();
clientBuilder.setEndpointConfiguration(config);
AWSSecretsManager  client  =  clientBuilder.build();     
 
ObjectMapper  objectMapper  =  new  ObjectMapper();
 
JsonNode  secretsJson  =  null;
 
ByteBuffer  binarySecretData;
 
GetSecretValueRequest  getSecretValueRequest  =  new  GetSecretValueRequest().withSecretId(secretName); 
 
GetSecretValueResult  getSecretValueResponse  =  null;
 
try  {
     getSecretValueResponse  =  client.getSecretValue(getSecretValueRequest);
    }
 
catch  (ResourceNotFoundException  e)  {
     log.error("The requested secret "  +  secretName  +  " was not found");
    }   
 
catch  (InvalidRequestException  e)  {    
     log.error("The request was invalid due to: "  +  e.getMessage());
     }   
 
catch  (InvalidParameterException  e)  {    
     log.error("The request had invalid params: "  +  e.getMessage());
     }
if  (getSecretValueResponse  ==  null)  {    
     return  null;
     }  // Decrypted secret using the associated KMS key // Depending on whether the secret was a string or binary, one of these fields will be populated    
       
 
String secret = getSecretValueResponse.getSecretString();  
 
if (secret != null) {  
     try {        
                secretsJson  =  objectMapper.readTree(secret);    
           }   
 
     catch  (IOException  e)  {        
                log.error("Exception while retrieving secret values: "  +  e.getMessage());    
           }
}   
 
else  {    
     log.error("The Secret String returned is null");  
 
     return null;      
 
     }
     String  host  =  secretsJson.get("host").textValue();
     String  port  =  secretsJson.get("port").textValue();
     String  dbname  =  secretsJson.get("dbname").textValue();
     String  username  =  secretsJson.get("username").textValue();
     String  password  =  secretsJson.get("password").textValue();
}
```