(Java)亚马逊:如何解决“仅允许一种身份验证机制;仅X-Amz-Algorithm查询参数...”

时间:2018-07-26 15:24:09

标签: java redirect amazon-s3 amazon url-redirection

Amazon API通常为您提供一个指向资源的URL,该URL给出了一个具有30s寿命的重定向URL作为重定向。但是,通常会继承原始请求的标头,从而导致此错误。重定向是通过已签名的重定向URL和身份验证标头中的身份验证进行处理的。

2 个答案:

答案 0 :(得分:0)

使用下面的静态函数可以解决问题。

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;


import static org.apache.commons.io.FileUtils.copyURLToFile;

/**  To intercept AMS redirects to S3, which requires removing authorization header since redirect is signed
 *
 *   Counteracts the error message
 *   <Error><Code>InvalidArgument</Code><Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
 **/
public class AMSDownloadInterceptor implements HttpResponseInterceptor {

    String filePath;
    int count;

    public AMSDownloadInterceptor(String filenameAndPath) {
        filePath = filenameAndPath;
        count = 0;
    }

    /**
     *      * This function call has done, HttpURLConnection.setFollowRedirects(false);
     *      * you may need to HttpURLConnection.setFollowRedirects(true);
     * @param httpResponse
     * @param httpContext
     * @throws HttpException
     * @throws IOException
     */
    @Override
    public void process(HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {

        //do nothing on the subsequent redirectURL call
        if (count == 0) {
            //TODO is this too hack-a-licious to not be robust?
            String redirectUrl = httpContext.getAttribute("http.response").toString().split(",")[5].substring(11);
            System.err.println("\t\t\tDownloading to\t"+filePath+"\tfrom\t" +redirectUrl);
            copyURLToFile(new URL(redirectUrl), new File(filePath));
            HttpURLConnection.setFollowRedirects(false);
        }
        count++;
    }

    /**
     * Fetch a file requiring authentication to a temporary redirect (307)
     * Response modified to return status 200, but is not perfect
     *

     *
     * @param amazonS3URL
     * @param filenameAndPath
     * @param accessToken       authentication token
     * @param AMS_API_SCOPE     currently "1903489005170091"
     * @return  modified HttpResponse, overriding the failed second http call to malformed redirect URL
     */
    public static HttpResponse downloadViaRedirect(String amazonS3URL, String filenameAndPath, String accessToken, String AMS_API_SCOPE) throws IOException {

        HttpResponse result = null;
        AMSDownloadInterceptor downloader = new AMSDownloadInterceptor(filenameAndPath);
        HttpClient clientFileInterceptor = HttpClientBuilder.create().addInterceptorFirst(downloader).build();
        HttpGet get = new HttpGet(amazonS3URL);
        get.setHeader(new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken));
        get.setHeader(new BasicHeader("Amazon-Advertising-API-Scope", AMS_API_SCOPE));

        //HttpURLConnection.setFollowRedirects(false);
        HttpResponse hr = clientFileInterceptor.execute(get);
        HttpURLConnection.setFollowRedirects(true);

        hr.setStatusCode(200);
        hr.setReasonPhrase("URL redirect downloaded "+amazonS3URL);
        hr.setEntity(new HttpEntity() {
            String result = "{\"statusDetails\":\"File successfully downloaded.\",\"status\":\"SUCCESS\"}";
            @Override
            public boolean isRepeatable() {
                return true;
            }

            @Override
            public boolean isChunked() {
                return false;
            }

            @Override
            public long getContentLength() {
                return result.length();
            }

            @Override
            public Header getContentType() {
                return null;
            }

            @Override
            public Header getContentEncoding() {
                return null;
            }

            @Override
            public InputStream getContent() throws IOException, UnsupportedOperationException {
                return new ByteArrayInputStream(result.getBytes("UTF-8"));
            }

            @Override
            public void writeTo(OutputStream outputStream) throws IOException {

            }

            @Override
            public boolean isStreaming() {
                return false;
            }

            @Override
            public void consumeContent() throws IOException {

            }
        });

        return result;
    }
}

答案 1 :(得分:0)

这是我在Java中进行的操作。

    // Set up the initial GET request with the required Amazon authorization headers
    HttpGet httpGet1 = new HttpGet(downloadUrl);
    httpGet1.setHeaders(authHeaders);

    // We need to tell HttpClient NOT to follow redirects because we need to handle this ourselves
    HttpParams params = new BasicHttpParams();
    params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
    httpGet1.setParams(params);

    String location = null; // this will hold the location where you can download the file

    try (CloseableHttpResponse response = httpClient.execute(httpGet1)) {

        // Check if we have a redirect
        if (response.getStatusLine().getStatusCode() == 307) {
            // The Location header has the download URL we need
            Header hdr = response.getFirstHeader("Location");

            // This is the URL we are looking for 
            location = hdr.getValue();
        }
    }

    // Now that we have the S3 download location we can build a GET request to fetch the contents
    // Do NOT include auth headers because the query string contains the auth parameters
    // This is a very short lived auth grant so we need to send the request within 30 secs

    HttpGet httpGet2 = new HttpGet(location);

    try (CloseableHttpResponse response = httpClient.execute(httpGet2)) {

        HttpEntity entity = response.getEntity();

        // Handle the stream. In this case it will be gzipped
        GZIPInputStream gis = new GZIPInputStream(entity.getContent());

        // Do what you need to do with the data

        // ...
    }