

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

# 更改数据模型
<a name="data-model"></a>

**注意**  
我们的客户端加密库已[重命名为 AWS 数据库加密 SDK](DDBEC-rename.md)。以下主题提供有关适用于 Java 的 DynamoDB 加密客户端版本 1.*x*—2.*x* 以及适用于 Python 的 DynamoDB 加密客户端版本 1.*x*—3.*x* 的信息。有关更多信息，请参阅[适用于 DynamoDB 的AWS 数据库加密 SDK 版本支持](legacy-dynamodb-encryption-client.md#legacy-support)。

每次加密或解密项目时，您需要提供[属性操作](DDBEC-legacy-concepts.md#legacy-attribute-actions)，这些操作告诉 DynamoDB 加密客户端要加密并签名的属性、要签名（但不加密）的属性以及要忽略的属性。属性操作不会保存在加密的项目中，并且 DynamoDB 加密客户端不会自动更新您的属性操作。

**重要**  
DynamoDB 加密客户端不支持对现有的未加密 DynamoDB 表数据进行加密。

每当您更改数据模型时（也即，在表项目中添加或删除属性时），都有可能出错。如果您指定的属性操作未考虑到该项目中的所有属性，则该项目可能不会按您希望的方式进行加密和签名。更重要的是，如果您在解密项目时提供的属性操作与在加密项目时提供的属性操作不同，则签名验证可能会失败。

例如，如果用于加密项目的属性操作告知其签署 `test` 属性，则项目中的签名将包含 `test` 属性。但是，如果用于解密项目的属性操作未考虑到 `test` 属性，则验证会失败，因为客户端将尝试验证与包含 `test` 属性的签名。

当多个应用程序读取和写入相同的 DynamoDB 项目时，这是一个特别的问题，因为 DynamoDB 加密客户端必须为所有应用程序中的项目计算相同的签名。对于任何分布式应用程序来说，这也是一个问题，因为属性操作的更改必须传播到所有主机。即使您的 DynamoDB 表是由一个主机在一个过程中访问的，但如果项目变得更加复杂，则建立最佳实践过程也将有助于防止错误。

为避免签名验证错误阻止您读取表项目，请使用以下指南。
+ [添加属性](#add-attribute) — 如果新的属性更改了属性操作，请在将新属性包括在项目中之前完全部署属性操作更改。
+ [移除属性](#remove-attribute) — 如果您停止在项目中使用属性，请不要更改属性操作。
+ 更改操作 — 使用属性操作配置对表项目进行加密后，如果不重新加密表中的每个项目，就无法安全地更改默认操作或现有属性的操作。

签名验证错误可能很难解决，因此最好的方法是防止它们发生。

**Topics**
+ [添加属性](#add-attribute)
+ [删除属性](#remove-attribute)

## 添加属性
<a name="add-attribute"></a>

在向表项目添加新属性时，可能需要更改属性操作。为了防止签名验证错误，我们建议您分两步实施此更改。在开始第二阶段之前，请验证第一阶段是否已完成。

1. 在读取或写入表的所有应用程序中更改属性操作。部署这些更改并确认更新已传播到所有目标主机。

1. 将值写入表项目中的新属性。

这种两阶段方法可确保所有应用程序和主机具有相同的属性操作，并且在遇到新属性之前将计算相同的签名。即使该属性的操作为*不执行任何操作*（不加密或签名），这也很重要，因为某些加密程序的默认设置是加密和签名。

以下示例显示此过程中第一阶段的代码。它们添加了一个新的项目属性 `link`，该属性存储指向另一个表项目的链接。由于此链接必须保持为纯文本格式，因此该示例向其分配仅签名操作。完全部署此更改，然后验证所有应用程序和主机都具有新的属性操作之后，可以开始在表项目中使用 `link` 属性。

------
#### [ Java DynamoDB Mapper ]

当使用 `DynamoDB Mapper` 和 `AttributeEncryptor` 时，默认情况下，除主键以外的所有属性均加密并签名，而主键已签名但未加密。要指定仅签名操作，请使用 `@DoNotEncrypt` 注释。

本示例将 `@DoNotEncrypt` 注释用于新的 `link` 属性。

```
@DynamoDBTable(tableName = "ExampleTable")
public static final class DataPoJo {
  private String partitionAttribute;
  private int sortAttribute;
  private String link;

  @DynamoDBHashKey(attributeName = "partition_attribute")
  public String getPartitionAttribute() {
    return partitionAttribute;
  }
    
  public void setPartitionAttribute(String partitionAttribute) {
    this.partitionAttribute = partitionAttribute;
  }

  @DynamoDBRangeKey(attributeName = "sort_attribute")
  public int getSortAttribute() {
    return sortAttribute;
  }

  public void setSortAttribute(int sortAttribute) {
    this.sortAttribute = sortAttribute;
  }

  @DynamoDBAttribute(attributeName = "link")
  @DoNotEncrypt
  public String getLink() {
    return link;
  }

  public void setLink(String link) {
    this.link = link;
  }

  @Override
  public String toString() {
    return "DataPoJo [partitionAttribute=" + partitionAttribute + ",
        sortAttribute=" + sortAttribute + ",
        link=" + link + "]";
  }
}
```

------
#### [ Java DynamoDB encryptor ]

 在较低级别的 DynamoDB 加密程序中，必须为每个属性设置操作。本示例使用一个开关语句，其中默认值为 `encryptAndSign`，并为分区键、排序键和新的 `link` 属性指定了例外。在此示例中，如果在使用链接属性代码之前未完全部署它，则链接属性将由某些应用程序加密和签名，而仅由其他应用程序签名。

```
for (final String attributeName : record.keySet()) {
    switch (attributeName) {
        case partitionKeyName:
            // fall through to the next case
        case sortKeyName:
            // partition and sort keys must be signed, but not encrypted
            actions.put(attributeName, signOnly);
            break;
        case "link":
            // only signed
            actions.put(attributeName, signOnly);
            break;
        default:
            // Encrypt and sign all other attributes
            actions.put(attributeName, encryptAndSign);
            break;
    }
}
```

------
#### [ Python ]

在适用于 Python 的 DynamoDB 加密客户端中，您可以为所有属性指定默认操作，然后指定例外。

如果您使用的是 Python [客户端帮助程序类](python-using.md#python-helpers)，则无需指定主键属性的属性操作。该客户端帮助程序类阻止您加密主键。但是，如果不使用客户端帮助程序类，则必须在分区键和排序键上设置 SIGN\$1ONLY 操作。如果您不小心加密了分区键或排序键，那么在没有全表扫描的情况下将无法恢复数据。

本示例为新的 `link` 属性指定一个例外，该例外将获取 `SIGN_ONLY` 操作。

```
actions = AttributeActions(
    default_action=CryptoAction.ENCRYPT_AND_SIGN,
    attribute_actions={
      'example': CryptoAction.DO_NOTHING,  
      'link': CryptoAction.SIGN_ONLY
    }
)
```

------

## 删除属性
<a name="remove-attribute"></a>

如果您在使用 DynamoDB 加密客户端加密的项目中不再需要某个属性，则可以停止使用该属性。但是，请勿删除或更改该属性的操作。如果这样做，然后遇到具有该属性的项目，则为该项目计算的签名将与原始签名不匹配，并且签名验证将失败。

尽管您可能很想从代码中删除该属性的所有痕迹，但请添加一条注释，指出不再使用该项目，而不是删除它。即使您进行全表扫描以删除该属性的所有实例，具有该属性的加密项目也可能会缓存或在配置中的某个地方处于正在进行状态。