【问题标题】:Sign S3 upload requests for the browser via the AWS-Java SDK通过 AWS-Java 开发工具包签署浏览器的 S3 上传请求
【发布时间】:2017-08-29 02:02:22
【问题描述】:

我有一个使用 EmberJS 构建的前端客户端,特别是 ember-uploader 来处理将文件直接上传到 S3。我卡住的地方是,在请求发送到亚马逊之前,我似乎无法使用我的后端服务器(A java Dropwizard 微服务)正确签署请求。

我知道我可以创建一个GeneratePresignedUrlRequest,但我使用的前端库特别想要从服务器返回一个 json 对象,所以我试图将 GeneratePresignedUrlRequest 拆分为一个对象。

目前一切似乎都很好,但我错过了policy,因为我无法锻炼如何正确创建它。

private SignRequestObject createSignRequestObject(List<NameValuePair> valuePairs) {
    SignRequestObject request = new SignRequestObject();

    request.setKey("test.txt");
    request.setBucket("test-bucket");
    request.setPolicy("?");

    for (NameValuePair pairs : valuePairs) {
        if (pairs.getName().equals("X-Amz-Credential")) {
            request.setCredentials(pairs.getValue());
        }

        if (pairs.getName().equals("X-Amz-Signature")) {
            request.setSignature(pairs.getValue());
        }

        if (pairs.getName().equals("X-Amz-Algorithm")) {
            request.setAlgorithm(pairs.getValue());
        }

        if (pairs.getName().equals("X-Amz-Date")) {
            request.setDate(pairs.getValue());
        }
    }

    return request;
}

valuePairs 来自GeneratePresignedUrlRequest

private String createSignedUrl() {
    GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest("test-bucket", "test.txt");
    generatePresignedUrlRequest.setMethod(HttpMethod.PUT);

    return amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString();
}

根据 ember-uploader 的 wiki,我希望策略对象看起来像:

// Ruby example, but shouldn't matter
        {
          expiration: @expires,
          conditions: [
            { bucket: 'sandbox' },
            { acl: 'public-read' },
            { expires: @expires },
            { success_action_status: '201' },
            [ 'starts-with', '$key', '' ],
            [ 'starts-with', '$Content-Type', '' ],
            [ 'starts-with', '$Cache-Control', '' ],
            [ 'content-length-range', 0, 524288000 ]
          ]
      )

我应该尝试自己构建它还是 aws-sdk 有用于此的方法?我经常看到AWS Signature Version 4,但也不知道如何使用它。

尝试通过浏览器上传时,我收到了来自亚马逊的 403。

【问题讨论】:

  • 我强烈推荐使用 Jets3t。
  • 您能否详细说明 jets3t 的哪一部分适用?我通过文档看到的最接近的是 S3 POST 表单,这是我所追求的,但我不想通过后端服务呈现表单
  • 我强烈建议使用后端至少创建签名,在这种情况下,您将使用任何必要的参数向服务器发送 AJAX 帖子,服务器将签名发回,然后您将组合的表单和签名提供给您的客户端上传器。
  • 在 Jet3ts API 中找不到任何可以让我在不创建完整表单对象的情况下创建签名的地方。您能否提供代码示例或指向 API 中可以执行此操作的位置的链接?
  • 嗨,我正处于一个类似的难题中,我的 Java 端工作基于 Amazon 提供的这些 Signature Calculation Examples Using Java (AWS Signature Version 4)。对于客户来说,这是一个两步的过程。 1) 客户端向后端应用程序询问签名版本 4“策略”。 2) 客户端使用该策略根据Browser-Based Uploads Using POST POST 到 S3

标签: java amazon-web-services amazon-s3 aws-sdk


【解决方案1】:

我解决了这个问题并为它写了一个小 guice 模块。然后在对后端的 get 请求中从 Repository 类调用它。

// 资源

public class SignResource {

    private final SignRepository repository;

    @Inject
    public SignResource(SignRepository repository) {
        this.repository = repository;
    }

    @GET
    public Response signPOST(@QueryParam("type") String type) {
        String signRequest = repository.signRequest(type);
        return Response.status(Response.Status.OK).entity(signRequest).build();
    }
}

// 存储库

public class SignRepository {

    @Inject
    private SignService signService;

    public SignRepository() {
    }

    public String signRequest(String contentType) {
        return signService.signRequest(contentType);
    }
}

// 实现

public class SignServiceImpl implements SignService {

    private String awsBucket;
    private String awsAccessKey;
    private String awsSecretKey;

    SignServiceImpl(AmazonConfiguration amazon) {
        awsSecretKey = amazon.getSecret();
        awsAccessKey = amazon.getAccess();
        awsBucket = amazon.getBucket();
    }

    @Override
    public String signRequest(String contentType) {
        final String randomFileName = createRandomName();

        String policy = createPolicy(randomFileName, contentType);

        SignRequest signRequest = new SignRequest();
        signRequest.setAwsAccessKeyId(awsAccessKey);
        signRequest.setPolicy(policy);
        signRequest.setSignature(ServiceUtils.signWithHmacSha1(awsSecretKey, policy));
        signRequest.setBucket(awsBucket);
        signRequest.setKey(randomFileName);
        signRequest.setAcl("public-read");
        signRequest.setContentType(contentType);
        signRequest.setExpires(createExpireTime().toString());
        signRequest.setSuccessActionStatus("201");

        return createJsonString(signRequest);
    }

    private String createPolicy(String randomFileName, String contentType) {
        try {
            String[] conditions = {
                S3Service.generatePostPolicyCondition_Equality("bucket", awsBucket),
                S3Service.generatePostPolicyCondition_Equality("key", randomFileName),
                S3Service.generatePostPolicyCondition_Equality("acl", "public-read"),
                S3Service.generatePostPolicyCondition_Equality("expires", createExpireTime().toString()),
                S3Service.generatePostPolicyCondition_Equality("content-Type", contentType),
                S3Service.generatePostPolicyCondition_Equality("success_action_status", "201"),
                S3Service.generatePostPolicyCondition_AllowAnyValue("cache-control")
            };

            String policyDocument = "{\"expiration\": \"" + ServiceUtils.formatIso8601Date(createExpireTime()) + "\", \"conditions\": [" + ServiceUtils.join(conditions, ",") + "]}";

            return ServiceUtils.toBase64(policyDocument.getBytes(Constants.DEFAULT_ENCODING));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return null;
    }

    private String createRandomName() {
        return UUID.randomUUID().toString();
    }

    private Date createExpireTime() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR, 24);

        return cal.getTime();
    }

    private String createJsonString(SignRequest request) {
        ObjectMapper mapper = new ObjectMapper();
        String json = null;

        try {
            json = mapper.writeValueAsString(request);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return json;
    }
}

// 服务

public interface SignService {
    String signRequest(String contentType);
}

// 模块

public class SignServiceModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(SignService.class).toProvider(SignServiceProvider.class).asEagerSingleton();
    }
}

// 提供者

public class SignServiceProvider implements Provider<SignService> {

    @Inject
    private SwordfishConfiguration configuration;

    @Override
    public SignService get() {
        return new SignServiceImpl(configuration.getAmazon());
    }
}

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2016-05-10
    • 2016-02-11
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 2014-02-26
    • 2014-01-09
    • 2014-11-16
    相关资源
    最近更新 更多