2017-04-04 84 views
0

我有一個使用EmberJS構建的前端客戶端,特別是ember-uploader來處理將文件直接上傳到S3。在卡住的地方,我似乎無法使用我的後端服務器(Java Dropwizard微服務)在請求亞馬遜前正確簽署請求。通過AWS-Java SDK爲瀏覽器簽名S3上傳請求

我知道我可以創建一個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(); 
} 

未來根據燼,上傳的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回。

+0

我強烈建議您使用的JetS3t。 – chrylis

+0

你能否擴展jets3t的哪些部分適用?我能看到的最接近的文檔是S3 POST表單,這是我之後的一種,但我不想通過後端服務呈現表單 –

+0

我強烈建議使用後端創建至少簽名,在這種情況下,您可以使用任何必要的參數向服務器發送AJAX帖子,服務器將簽名發回,然後將組合的表單和簽名提供給客戶端上傳器。 – chrylis

回答

0

我解決了這個問題,併爲它寫了一個小的guice模塊。然後在獲取請求時從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()); 
    } 
} 
相關問題