

# 使用适用于 S3 on Outposts 的预签名 URL
<a name="S3OutpostsPresignedURL"></a>

要授予对 Outpost 本地存储对象的限时访问权限而不更新存储桶策略，您可以使用预签名 URL。借助预签名 URL，作为存储桶的所有者，您可以与您虚拟私有云（VPC）中的个人共享对象，或者向其授予上传或删除对象的权限。

使用 AWS SDK 或 AWS Command Line Interface（AWS CLI）创建预签名 URL 时，您会将该 URL 与某个特定的操作关联。您还可以通过选择自定义到期时间来授予对预签名 URL 的限时访问权限，自定义到期时间最短可为 1 秒，最长可为 7 天。共享预签名 URL 时，VPC 中的个人可以执行嵌入在 URL 中的操作，如同他们就是原始签名用户。URL 在到达其到期时间时将会过期，不再有效。

## 限制预签名 URL 功能
<a name="S3OutpostsPresignedUrlUploadObjectLimitCapabilities"></a>

预签名 URL 的功能受创建它的用户的权限所限制。预签名 URL 实质上是一种不记名令牌，向持有相关 URL 的人授予了访问权限。因此，我们建议您适当地保护它们。

**AWS 签名版本 4（SigV4）**  
使用 AWS 签名版本 4（SigV4）对预签名 URL 请求进行身份认证时要强制执行特定的行为，您可以在存储桶策略和访问点策略中使用条件键。例如，您可以创建一个使用 `s3-outposts:signatureAge` 条件的存储桶策略，以在相关签名的存在时间超过 10 分钟时，拒绝对 `example-outpost-bucket` 存储桶中对象的任何 Amazon S3 on Outposts 预签名 URL 请求。要使用此示例，请将 *`user input placeholders`* 替换为您自己的信息。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "Deny a presigned URL request if the signature is more than 10 minutes old",
            "Effect": "Deny",
            "Principal": {"AWS":"444455556666"},
            "Action": "s3-outposts:*",
            "Resource": "arn:aws:s3-outposts:us-east-1:111122223333:outpost/op-01ac5d28a6a232904/bucket/example-outpost-bucket/object/*",
            "Condition": {
                "NumericGreaterThan": {"s3-outposts:signatureAge": 600000},
                "StringEquals": {"s3-outposts:authType": "REST-QUERY-STRING"}
            }
        }
    ]
}
```

------

有关在使用签名版本 4 对预签名 URL 请求进行身份认证时，可用于强制执行特定行为的条件键和其他示例策略的列表，请参阅 [AWS 签名版本 4（SigV4）身份认证特定的策略键](s3-outposts-bucket-policy-s3-sigv4-conditions.md)。

**网络路径限制**  
如果要将预签名 URL 的使用和所有 S3 on Outposts 访问限定为特定的网络路径，您可以编写要求使用特定网络路径的策略。要对发起调用的 IAM 主体设置限制，您可以使用基于身份的 AWS Identity and Access Management（IAM）策略（例如用户、组或角色策略）。要对 S3 on Outposts 资源设置限制，您可以使用基于资源的策略（例如，存储桶和访问点策略）。

对 IAM 主体实施网络路径限制后，要求拥有这些凭证的用户从指定的网络发出请求。对存储桶或接入点实施限制后，要求对该资源的所有请求都必须来自指定的网络。这些限制也适用于预签名 URL 以外的场景。

您使用的 IAM 全局条件取决于端点的类型。如果您使用适用于 S3 on Outposts 的公有端点，请使用 `aws:SourceIp`。如果您使用适用于 S3 on Outposts 的 VPC 端点，请使用 `aws:SourceVpc` 或 `aws:SourceVpce`。

以下 IAM policy 语句要求主体仅从指定的网络范围访问。AWS使用此策略语句时，所有访问都必须来自该范围。这包括有人使用适用于 S3 on Outposts 的预签名 URL 的情况。要使用此示例，请将 *`user input placeholders`* 替换为您自己的信息。

```
{
    "Sid": "NetworkRestrictionForIAMPrincipal",
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {
        "NotIpAddressIfExists": {"aws:SourceIp": "IP-address-range"},
        "BoolIfExists": {"aws:ViaAWSService": "false"}
    }
}
```

有关使用 `aws:SourceIP` AWS 全局条件键将对 S3 on Outposts 存储桶的访问限定为特定网络范围的示例存储桶策略，请参阅[使用 S3 on Outposts 设置 IAM](S3OutpostsIAM.md)。

## 谁可以创建预签名 URL
<a name="S3Outpostswho-presigned-url"></a>

具有有效安全凭证的任何人都可以创建预签名 URL。但要使 VPC 中的用户成功地访问对象，必须由拥有执行预签名 URL 所基于的操作权限的人创建该预签名 URL。

您可以使用下面的凭证创建预签名 URL：
+ **IAM 实例配置文件** – 有效期最长 6 小时。
+ **AWS Security Token Service** – 使用永久凭证（例如，AWS 账户 根用户或 IAM 用户的凭证）签名时，有效期最长 36 小时。
+ **IAM 用户** – 使用 AWS 签名版本 4 时，有效期最长 7 天。

  要创建有效期最长 7 天的预签名 URL，请首先为所使用的开发工具包委托 IAM 用户凭证（访问密钥和秘密密钥）。然后使用 AWS 签名版本 4 生成预签名 URL。

**注意**  
如果您已使用临时令牌创建了预签名 URL，则此 URL 将在令牌过期时过期，即使您使用更晚的到期时间创建了该 URL。
由于预签名 URL 将向持有该 URL 的任何人授予访问 S3 on Outposts 存储桶的权限，我们建议您妥善保护它们。有关保护预签名 URL 的更多信息，请参阅 [限制预签名 URL 功能](#S3OutpostsPresignedUrlUploadObjectLimitCapabilities)。

## S3 on Outposts 何时检查预签名 URL 的到期日期和时间？
<a name="S3Outpostspresigned-url-when-checked"></a>

在发出 HTTP 请求时，S3 on Outposts 会检查签名 URL 的到期日期和时间。例如，如果客户端刚好在到期时间之前开始下载某个大型文件，即使在下载过程中超过到期时间，下载也会继续进行。但如果连接断开，在客户端试图在超过到期时间后重新开始下载，则下载将会失败。

有关使用预签名 URL 共享或上传对象的更多信息，请参阅以下主题。

**Topics**
+ [限制预签名 URL 功能](#S3OutpostsPresignedUrlUploadObjectLimitCapabilities)
+ [谁可以创建预签名 URL](#S3Outpostswho-presigned-url)
+ [S3 on Outposts 何时检查预签名 URL 的到期日期和时间？](#S3Outpostspresigned-url-when-checked)
+ [使用预签名 URL 共享对象](S3OutpostsShareObjectPresignedURL.md)
+ [生成预签名 URL 以将对象上传到 S3 on Outposts 存储桶](S3OutpostsPresignedUrlUploadObject.md)

# 使用预签名 URL 共享对象
<a name="S3OutpostsShareObjectPresignedURL"></a>

要授予对 Outpost 本地存储对象的限时访问权限而不更新存储桶策略，您可以使用预签名 URL。借助预签名 URL，作为存储桶的所有者，您可以与您虚拟私有云（VPC）中的个人共享对象，或者向其授予上传或删除对象的权限。

使用 AWS SDK 或 AWS Command Line Interface（AWS CLI）创建预签名 URL 时，您会将该 URL 与某个特定的操作关联。您还可以通过选择自定义到期时间来授予对预签名 URL 的限时访问权限，自定义到期时间最短可为 1 秒，最长可为 7 天。共享预签名 URL 时，VPC 中的个人可以执行嵌入在 URL 中的操作，如同他们就是原始签名用户。URL 在到达其到期时间时将会过期，不再有效。



创建预签名 URL 时，必须提供您的安全凭证，然后指定以下内容：
+ 该 Amazon S3 on Outposts 存储桶的一个访问点 Amazon 资源名称（ARN）
+ 一个对象键
+ 一个 HTTP方法（`GET` 用于下载对象）
+ 一个到期日期和时间

预签名 URL 仅在指定的有效期内有效。也就是说，您必须在到期日期和时间到达之前启动该 URL 允许的操作。在到期日期和时间到达之前，您可以多次使用预签名 URL。如果您已使用临时令牌创建了预签名 URL，则此 URL 将在令牌过期时过期，即使您使用更晚的到期时间创建了该 URL。

虚拟私有云（VPC）中有权访问预签名 URL 的用户可以访问对象。例如，如果您在桶中具有一段视频，并且桶和对象均为私有，您可以通过生成预签名的 URL 来与其他用户共享视频。由于预签名 URL 将向持有该 URL 的任何人授予访问 S3 on Outposts 存储桶的权限，我们建议您妥善保护这些 URL。有关保护预签名 URL 的更多详细信息，请参阅 [限制预签名 URL 功能](S3OutpostsPresignedURL.md#S3OutpostsPresignedUrlUploadObjectLimitCapabilities)。

具有有效安全凭证的任何人都可以创建预签名 URL。但必须由拥有执行预签名 URL 所基于的操作权限的人创建该预签名 URL。有关更多信息，请参阅 [谁可以创建预签名 URL](S3OutpostsPresignedURL.md#S3Outpostswho-presigned-url)。

您可以使用 AWS SDK 和 AWS CLI 生成预签名 URL 以共享 S3 on Outposts 存储桶中的对象。有关更多信息，请参阅以下示例。

## 使用 AWS SDK
<a name="S3OutpostsShareObjectPreSignedURLSDK"></a>

您可以使用 AWS SDK 生成可以提供给其他人的预签名 URL，以便他们可以检索对象。

**注意**  
使用 AWS SDK 生成预签名 URL 时，预签名 URL 的最长到期时间为创建之时起 7 天。

------
#### [ Java ]

**Example**  
以下示例将生成一个预签名 URL，您可以将其提供给其他人，以便他们可以检索 S3 on Outposts 存储桶的对象。有关更多信息，请参阅 [使用适用于 S3 on Outposts 的预签名 URL](S3OutpostsPresignedURL.md)。要使用此示例，请将 *`user input placeholders`* 替换为您自己的信息。  

```
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;

import java.io.IOException;
import java.net.URL;
import java.time.Instant;

public class GeneratePresignedURL {

    public static void main(String[] args) throws IOException {
        Regions clientRegion = Regions.DEFAULT_REGION;
        String accessPointArn = "*** access point ARN ***";
        String objectKey = "*** object key ***";

        try {
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withRegion(clientRegion)
                    .withCredentials(new ProfileCredentialsProvider())
                    .build();

            // Set the presigned URL to expire after one hour.
            java.util.Date expiration = new java.util.Date();
            long expTimeMillis = Instant.now().toEpochMilli();
            expTimeMillis += 1000 * 60 * 60;
            expiration.setTime(expTimeMillis);

            // Generate the presigned URL.
            System.out.println("Generating pre-signed URL.");
            GeneratePresignedUrlRequest generatePresignedUrlRequest =
                    new GeneratePresignedUrlRequest(accessPointArn, objectKey)
                            .withMethod(HttpMethod.GET)
                            .withExpiration(expiration);
            URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

            System.out.println("Pre-Signed URL: " + url.toString());
        } catch (AmazonServiceException e) {
            // The call was transmitted successfully, but Amazon S3 couldn't process 
            // it, so it returned an error response.
            e.printStackTrace();
        } catch (SdkClientException e) {
            // Amazon S3 couldn't be contacted for a response, or the client
            // couldn't parse the response from Amazon S3.
            e.printStackTrace();
        }
    }
}
```

------
#### [ .NET ]

**Example**  
以下示例将生成一个预签名 URL，您可以将其提供给其他人，以便他们可以检索 S3 on Outposts 存储桶的对象。有关更多信息，请参阅 [使用适用于 S3 on Outposts 的预签名 URL](S3OutpostsPresignedURL.md)。要使用此示例，请将 *`user input placeholders`* 替换为您自己的信息。  

```
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using System;

namespace Amazon.DocSamples.S3
{
    class GenPresignedURLTest
    {
        private const string accessPointArn = "*** access point ARN ***"; 
        private const string objectKey = "*** object key ***";
        // Specify how long the presigned URL lasts, in hours.
        private const double timeoutDuration = 12;
        // Specify your bucket Region (an example Region is shown).
        private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2;
        private static IAmazonS3 s3Client;

        public static void Main()
        {
            s3Client = new AmazonS3Client(bucketRegion);
            string urlString = GeneratePreSignedURL(timeoutDuration);
        }
        static string GeneratePreSignedURL(double duration)
        {
            string urlString = "";
            try
            {
                GetPreSignedUrlRequest request1 = new GetPreSignedUrlRequest
                {
                    BucketName = accessPointArn,
                    Key = objectKey,
                    Expires = DateTime.UtcNow.AddHours(duration)
                };
                urlString = s3Client.GetPreSignedURL(request1);
            }
            catch (AmazonS3Exception e)
            {
                Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
            }
            return urlString;
        }
    }
}
```

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

使用适用于 Python (Boto3) 的 SDK 生成预签名 URL 以共享对象。例如，使用 Boto3 客户端和 `generate_presigned_url` 函数生成一个允许您 `GET` 对象的预签名 URL。

```
import boto3
    url = boto3.client('s3').generate_presigned_url(
    ClientMethod='get_object', 
    Params={'Bucket': 'ACCESS_POINT_ARN', 'Key': 'OBJECT_KEY'},
    ExpiresIn=3600)
```

有关使用适用于 Python 的 SDK（Boto3）生成预签名 URL 的更多信息，请参阅《AWS SDK for Python (Boto) API 参考》**中的 [Python](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.generate_presigned_url)。

------

## 使用 AWS CLI
<a name="S3OutpostsShareObjectPresignedCLI"></a>

以下示例 AWS CLI 命令将为一个 S3 on Outposts 存储桶生成一个预签名 URL。要使用此示例，请将 *`user input placeholders`* 替换为您自己的信息。

**注意**  
使用 AWS CLI 生成预签名 URL 时，预签名 URL 的最长到期时间为创建之时起 7 天。

```
aws s3 presign s3://arn:aws:s3-outposts:us-east-1:111122223333:outpost/op-01ac5d28a6a232904/accesspoint/example-outpost-access-point/mydoc.txt --expires-in 604800
```

有关更多信息，请参阅《AWS CLI 命令参考》中的 [presign](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/presign.html)。

# 生成预签名 URL 以将对象上传到 S3 on Outposts 存储桶
<a name="S3OutpostsPresignedUrlUploadObject"></a>

要授予对 Outpost 本地存储对象的限时访问权限而不更新存储桶策略，您可以使用预签名 URL。借助预签名 URL，作为存储桶的所有者，您可以与您虚拟私有云（VPC）中的个人共享对象，或者向其授予上传或删除对象的权限。

使用 AWS SDK 或 AWS Command Line Interface（AWS CLI）创建预签名 URL 时，您会将该 URL 与某个特定的操作关联。您还可以通过选择自定义到期时间来授予对预签名 URL 的限时访问权限，自定义到期时间最短可为 1 秒，最长可为 7 天。共享预签名 URL 时，VPC 中的个人可以执行嵌入在 URL 中的操作，如同他们就是原始签名用户。URL 在到达其到期时间时将会过期，不再有效。

创建预签名 URL 时，必须提供您的安全凭证，然后指定以下内容：
+ 该 Amazon S3 on Outposts 存储桶的一个访问点 Amazon 资源名称（ARN）
+ 一个对象键
+ 一个 HTP 方法（`PUT` 用于上传对象）
+ 一个到期日期和时间

预签名 URL 仅在指定的有效期内有效。也就是说，您必须在到期日期和时间到达之前启动该 URL 允许的操作。在到期日期和时间到达之前，您可以多次使用预签名 URL。如果您已使用临时令牌创建预签名 URL，则此 URL 将在令牌过期时过期，即使创建的 URL 的到期时间更晚也是如此。

如果预签名 URL 允许的操作由多个步骤构成（例如分段上传），则必须在到期时间前启动所有步骤。如果 S3 on Outposts 尝试使用某个已过期的 URL 启动某个步骤，您将会遇到错误。

虚拟私有云（VPC）中有权访问预签名 URL 的用户可上传对象。例如，VPC 中有权访问预签名 URL 的用户可以将对象上传到您的存储桶。由于预签名 URL 将向 VPC 中有权访问预签名 URL 的任何用户授予访问 S3 on Outposts 存储桶的权限，我们建议您妥善保护这些 URL。有关保护预签名 URL 的更多详细信息，请参阅 [限制预签名 URL 功能](S3OutpostsPresignedURL.md#S3OutpostsPresignedUrlUploadObjectLimitCapabilities)。

具有有效安全凭证的任何人都可以创建预签名 URL。但必须由拥有执行预签名 URL 所基于的操作权限的人创建该预签名 URL。有关更多信息，请参阅 [谁可以创建预签名 URL](S3OutpostsPresignedURL.md#S3Outpostswho-presigned-url)。

## 使用 AWS SDK 为 S3 on Outposts 对象操作生成预签名 URL
<a name="s3-outposts-presigned-urls-upload-examples"></a>

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
此示例演示了如何生成一个预签名 URL，以便在限定时间内用于将对象上传到某个 S3 on Outposts 存储桶。有关更多信息，请参阅 [使用适用于 S3 on Outposts 的预签名 URL](S3OutpostsPresignedURL.md)。  

```
    public static void signBucket(S3Presigner presigner, String outpostAccessPointArn, String keyName) {

        try {
            PutObjectRequest objectRequest = PutObjectRequest.builder()
                    .bucket(accessPointArn)
                    .key(keyName)
                    .contentType("text/plain")
                    .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))
                    .putObjectRequest(objectRequest)
                    .build();

            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);


            String myURL = presignedRequest.url().toString();
            System.out.println("Presigned URL to upload a file to: " +myURL);
            System.out.println("Which HTTP method must be used when uploading a file: " +
                    presignedRequest.httpRequest().method());

            // Upload content to the S3 on Outposts bucket by using this URL.
            URL url = presignedRequest.url();

            // Create the connection and use it to upload the new object by using the presigned URL.
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type","text/plain");
            connection.setRequestMethod("PUT");
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
            out.write("This text was uploaded as an object by using a presigned URL.");
            out.close();

            connection.getResponseCode();
            System.out.println("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception e) {
            e.getStackTrace();
        } catch (IOException e) {
            e.getStackTrace();
        }
    }
```

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

**适用于 Python 的 SDK（Boto3）**  
此示例演示了如何生成可在限定时间内执行某个 S3 on Outposts 操作的预签名 URL。有关更多信息，请参阅 [使用适用于 S3 on Outposts 的预签名 URL](S3OutpostsPresignedURL.md)。要使用该 URL 发出请求，请使用 `Requests` 程序包。  

```
import argparse
import logging
import boto3
from botocore.exceptions import ClientError
import requests

logger = logging.getLogger(__name__)


def generate_presigned_url(s3_client, client_method, method_parameters, expires_in):
    """
    Generate a presigned S3 on Outposts URL that can be used to perform an action.

    :param s3_client: A Boto3 Amazon S3 client.
    :param client_method: The name of the client method that the URL performs.
    :param method_parameters: The parameters of the specified client method.
    :param expires_in: The number of seconds that the presigned URL is valid for.
    :return: The presigned URL.
    """
    try:
        url = s3_client.generate_presigned_url(
            ClientMethod=client_method,
            Params=method_parameters,
            ExpiresIn=expires_in
        )
        logger.info("Got presigned URL: %s", url)
    except ClientError:
        logger.exception(
            "Couldn't get a presigned URL for client method '%s'.", client_method)
        raise
    return url


def usage_demo():
    logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

    print('-'*88)
    print("Welcome to the Amazon S3 on Outposts presigned URL demo.")
    print('-'*88)

    parser = argparse.ArgumentParser()
    parser.add_argument('accessPointArn', help="The name of the S3 on Outposts access point ARN.")
    parser.add_argument(
        'key', help="For a GET operation, the key of the object in S3 on Outposts. For a "
                    "PUT operation, the name of a file to upload.")
    parser.add_argument(
        'action', choices=('get', 'put'), help="The action to perform.")
    args = parser.parse_args()

    s3_client = boto3.client('s3')
    client_action = 'get_object' if args.action == 'get' else 'put_object'
    url = generate_presigned_url(
        s3_client, client_action, {'Bucket': args.accessPointArn, 'Key': args.key}, 1000)

    print("Using the Requests package to send a request to the URL.")
    response = None
    if args.action == 'get':
        response = requests.get(url)
    elif args.action == 'put':
        print("Putting data to the URL.")
        try:
            with open(args.key, 'r') as object_file:
                object_text = object_file.read()
            response = requests.put(url, data=object_text)
        except FileNotFoundError:
            print(f"Couldn't find {args.key}. For a PUT operation, the key must be the "
                  f"name of a file that exists on your computer.")

    if response is not None:
        print("Got response:")
        print(f"Status: {response.status_code}")
        print(response.text)

    print('-'*88)


if __name__ == '__main__':
    usage_demo()
```

------