亚马逊产品广告API错误(时间戳无效)

时间:2018-12-02 16:48:04

标签: spring api amazon

我正在尝试调用产品广告API,但是仍然出现相同的错误。

  

  InvalidParameterValue参数Timestamp的值2018-12-02T16%3A37%3A08.174%2B0000无效。原因:必须为ISO8601格式。7c46e224-8e98-4909-986b-cbedb9dd4d15

我已经阅读了与发现的错误相关的各种帖子,但似乎没有一种解决方案有效。下面是我使用的代码:

public class SignedRequestsHelper {

private static final String UTF8_CHARSET = "UTF-8";

/**
 * The HMAC algorithm required by Amazon
 */
private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

/**
 * This is the URI for the service, don't change unless you really know
 * what you're doing.
 */
private static final String REQUEST_URI = "/onca/xml";

/**
 * The sample uses HTTP GET to fetch the response. If you changed the sample
 * to use HTTP POST instead, change the value below to POST.
 */
private static final String REQUEST_METHOD = "GET";

private String endpoint = null;
private String awsAccessKeyId = null;
private String awsSecretKey = null;

private SecretKeySpec secretKeySpec = null;
private Mac mac = null;

/**
 * You must provide the three values below to initialize the helper.
 *
 * @param endpoint          Destination for the requests.
 * @param awsAccessKeyId    Your AWS Access Key ID
 * @param awsSecretKey      Your AWS Secret Key
 */
public SignedRequestsHelper(
        String endpoint,
        String awsAccessKeyId,
        String awsSecretKey
) throws IllegalArgumentException, UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException
{
    if (null == endpoint || endpoint.length() == 0)
    { throw new IllegalArgumentException("endpoint is null or empty"); }
    if (null == awsAccessKeyId || awsAccessKeyId.length() == 0)
    { throw new IllegalArgumentException("awsAccessKeyId is null or empty"); }
    if (null == awsSecretKey || awsSecretKey.length() == 0)
    { throw new IllegalArgumentException("awsSecretKey is null or empty"); }

    this.endpoint = endpoint.toLowerCase();
    this.awsAccessKeyId = awsAccessKeyId;
    this.awsSecretKey = awsSecretKey;

    byte[] secretyKeyBytes = this.awsSecretKey.getBytes(UTF8_CHARSET);
    this.secretKeySpec = new SecretKeySpec(secretyKeyBytes, HMAC_SHA256_ALGORITHM);
    this.mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
    this.mac.init(this.secretKeySpec);
}

/**
 * The construct is private since we'd rather use getInstance()
 */
private SignedRequestsHelper() {}

/**
 * This method signs requests in hashmap form. It returns a URL that should
 * be used to fetch the response. The URL returned should not be modified in
 * any way, doing so will invalidate the signature and Amazon will reject
 * the request.
 */
public String sign(Map<String, String> params) {
    // Let's add the AWSAccessKeyId and Timestamp parameters to the request.
    params.put("AWSAccessKeyId", this.awsAccessKeyId);
    params.put("Timestamp", this.timestamp());

    // The parameters need to be processed in lexicographical order, so we'll
    // use a TreeMap implementation for that.
    SortedMap<String, String> sortedParamMap = new TreeMap<String, String>(params);

    // get the canonical form the query string
    String canonicalQS = this.canonicalize(sortedParamMap);

    // create the string upon which the signature is calculated
    String toSign =
            REQUEST_METHOD + "\n"
                    + this.endpoint + "\n"
                    + REQUEST_URI + "\n"
                    + canonicalQS;

    // get the signature
    String hmac = this.hmac(toSign);
    String sig = this.percentEncodeRfc3986(hmac);

    // construct the URL
    String url =
            "http://" + this.endpoint + REQUEST_URI + "?" + canonicalQS + "&Signature=" + sig;

    return url;
}

/**
 * This method signs requests in query-string form. It returns a URL that
 * should be used to fetch the response. The URL returned should not be
 * modified in any way, doing so will invalidate the signature and Amazon
 * will reject the request.
 */
public String sign(String queryString) {
    // let's break the query string into it's constituent name-value pairs
    Map<String, String> params = this.createParameterMap(queryString);

    // then we can sign the request as before
    return this.sign(params);
}

/**
 * Compute the HMAC.
 *
 * @param stringToSign  String to compute the HMAC over.
 * @return              base64-encoded hmac value.
 */
private String hmac(String stringToSign) {
    String signature = null;
    byte[] data;
    byte[] rawHmac;
    try {
        data = stringToSign.getBytes(UTF8_CHARSET);
        rawHmac = mac.doFinal(data);
        Base64 encoder = new Base64();
        signature = new String(encoder.encode(rawHmac));
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(UTF8_CHARSET + " is unsupported!", e);
    }
    return signature;
}

/**
 * Generate a ISO-8601 format timestamp as required by Amazon.
 *
 * @return  ISO-8601 format timestamp.
 */
private String timestamp() {
    String timestamp = null;
    Calendar cal = Calendar.getInstance();
    DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
    dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
    timestamp = dfm.format(cal.getTime());

    return timestamp;
}

/**
 * Canonicalize the query string as required by Amazon.
 *
 * @param sortedParamMap    Parameter name-value pairs in lexicographical order.
 * @return                  Canonical form of query string.
 */
private String canonicalize(SortedMap<String, String> sortedParamMap) {
    if (sortedParamMap.isEmpty()) {
        return "";
    }

    StringBuffer buffer = new StringBuffer();
    Iterator<Map.Entry<String, String>> iter = sortedParamMap.entrySet().iterator();

    while (iter.hasNext()) {
        Map.Entry<String, String> kvpair = iter.next();
        buffer.append(percentEncodeRfc3986(kvpair.getKey()));
        buffer.append("=");
        buffer.append(percentEncodeRfc3986(kvpair.getValue()));
        if (iter.hasNext()) {
            buffer.append("&");
        }
    }
    String cannoical = buffer.toString();
    return cannoical;
}

/**
 * Percent-encode values according the RFC 3986. The built-in Java
 * URLEncoder does not encode according to the RFC, so we make the
 * extra replacements.
 *
 * @param s decoded string
 * @return  encoded string per RFC 3986
 */
private String percentEncodeRfc3986(String s) {
    String out;
    try {
        out = URLEncoder.encode(s, UTF8_CHARSET)
                .replace("+", "%20")
                .replace("*", "%2A")
                .replace("%7E", "~");
    } catch (UnsupportedEncodingException e) {
        out = s;
    }
    return out;
}

/**
 * Takes a query string, separates the constituent name-value pairs
 * and stores them in a hashmap.
 *
 * @param queryString
 * @return
 */
private Map<String, String> createParameterMap(String queryString) {
    Map<String, String> map = new HashMap<String, String>();
    String[] pairs = queryString.split("&");

    for (String pair: pairs) {
        if (pair.length() < 1) {
            continue;
        }

        String[] tokens = pair.split("=",2);
        for(int j=0; j<tokens.length; j++)
        {
            try {
                tokens[j] = URLDecoder.decode(tokens[j], UTF8_CHARSET);
            } catch (UnsupportedEncodingException e) {
            }
        }
        switch (tokens.length) {
            case 1: {
                if (pair.charAt(0) == '=') {
                    map.put("", tokens[0]);
                } else {
                    map.put(tokens[0], "");
                }
                break;
            }
            case 2: {
                map.put(tokens[0], tokens[1]);
                break;
            }
        }
    }
    return map;
}

}

@服务 公共类ProductService {

private static final String ASSOCIATE_TAG = "TAG";

private static final String BASE_URL = "";

private final SignedRequestsHelper signedRequestsHelper;

private final WebClient webClient;

public ProductService(SignedRequestsHelper signedRequestsHelper,
                      WebClient.Builder webClientBuilder) {
    this.signedRequestsHelper = signedRequestsHelper;

    HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(tcpClient ->
                    tcpClient.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP).host("localhost").port(8888)));
    ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
    this.webClient = webClientBuilder.baseUrl("http://" + PAApiConfig.ENDPOINT).clientConnector(connector).build();
}

public String getRequest() {
    String requestUrl = null;

    Map<String, String> params = new HashMap<>();

    params.put("AssociateTag", "lucaorlando00-21");
    params.put("BrowseNode", "1480106031");
    params.put("Operation", "ItemSearch");
    params.put("ResponseGroup", "Images,ItemAttributes,OfferFull");
    params.put("SearchIndex", "Beauty");
    params.put("Service", "AWSECommerceService");
    params.put("Condition", "New");
    params.put("Availability", "Available");
    params.put("IncludeReviewsSummary", "true");

    return signedRequestsHelper.sign(params);
}

@PostConstruct
public void tt() {
    this.webClient
            .get()
            .uri(getRequest())
            // .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE)
            .header(HttpHeaders.ACCEPT, "text/plain, */*; q=0.01")
            //.header(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate")
            .header(HttpHeaders.ACCEPT_LANGUAGE, "it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7")
            .retrieve()
            .bodyToMono(String.class)
            .doOnError(throwable -> ((WebClientResponseException)throwable).getResponseBodyAsString())
            .subscribe();
}

}

0 个答案:

没有答案