通过 AWS-Java SDK 为浏览器签署 S3 上传请求
Sign S3 upload requests for the browser via the AWS-Java SDK
我有一个使用 EmberJS 构建的前端客户端,特别是 ember-uploader 用于处理将文件直接上传到 S3。我被卡住的地方是,在发送到亚马逊之前,我似乎无法使用我的后端服务器(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;
}
值对来自 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。
我解决了这个问题并为它写了一个小的 guice 模块。然后根据对后端的获取请求从存储库 class 调用它。
// 资源
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());
}
}
我有一个使用 EmberJS 构建的前端客户端,特别是 ember-uploader 用于处理将文件直接上传到 S3。我被卡住的地方是,在发送到亚马逊之前,我似乎无法使用我的后端服务器(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;
}
值对来自 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。
我解决了这个问题并为它写了一个小的 guice 模块。然后根据对后端的获取请求从存储库 class 调用它。
// 资源
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());
}
}