【问题标题】:Invalid method signature when sending HTTP POST request发送 HTTP POST 请求时方法签名无效
【发布时间】:2013-04-09 18:03:58
【问题描述】:

好吧,这真是令人沮丧。我使用我的 API 密钥非常好。身份验证正常,生成令牌正常,会话密钥也正常。

但是当我运行playlist.create 时,播放列表没有创建。

我今天几乎从头开始,并重新编写了大部分代码。我成功创建了 16 个播放列表,没有错误。但是今天,相同的代码不起作用。我根本没碰它,但还是报错。

private static void buildPlaylist() {

    String mood = "Happy";
    System.out.println("\nMood is " + mood + "\n\n");

    String title = URLEncoder.encode(mood + " " + new Date().getTime(), "UTF-8");
    String description = URLEncoder.encode("For when you are " + mood + ". Created by MoodicPlayer.", "UTF-8");

    MessageDigest md = MessageDigest.getInstance("MD5");

    String apiSig = "api_key" + key + "description" + description + "methodplaylist.createsk" + sessionKey + "title" + title + secret;
    md.update(apiSig.getBytes());
    byte byteData[] = md.digest();
    //convert the byte to hex format
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < byteData.length; i++) {
        sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
    }
    String hashedSig = sb.toString();

    // FOR DEBUGGING
    System.out.println("api_key = " + key);
    System.out.println("api_sig = " + hashedSig);
    System.out.println("session key = " + sessionKey);
    // FOR DEBUGGING

    String urlParameters = "method=playlist.create&api_key="+ key + "&api_sig=" + hashedSig + "&description=" + description + "&sk=" + sessionKey + "&title=" + title;
    String request = "http://ws.audioscrobbler.com/2.0/";

    URL url = new URL(request);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();     
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setInstanceFollowRedirects(false); 
    connection.setRequestMethod("POST"); 
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
    connection.setRequestProperty("charset", "utf-8");
    connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
    connection.setUseCaches(false);

    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    wr.writeBytes(urlParameters);
    wr.flush();
    wr.close();

    InputStream is = null;
    Scanner s = null;
    try {
        if (connection.getResponseCode() != 200) {
            s = new Scanner(connection.getErrorStream());
        } else {
            is = connection.getInputStream();
            s = new Scanner(is);
        }
        s.useDelimiter("\\Z");
        String response = s.next();
        System.out.println("\nResponse: " + response + "\n\n");
        BufferedWriter out = new BufferedWriter(new FileWriter("requestCreate.xml"));
        out.write(response);
        out.close();
    } catch (IOException e2) {
        e2.printStackTrace();
    }

    // FOR DEBUGGING
    try {
        System.out.println("Response Code: " + connection.getResponseCode());
        System.out.println("Response Message: " + connection.getResponseMessage()); 
    } catch (IOException e) {
        e.printStackTrace();
    }
    // FOR DEBUGGING
    connection.disconnect();
}

还有输出:

Starting test for creating playlist...

Mood is Happy
api_key = xxxxx
api_sig = xxxxxx
session key = xxxxx

Response: <?xml version="1.0" encoding="utf-8"?>
<lfm status="failed">
<error code="13">
    Invalid method signature supplied
</error>
</lfm>


Response Code: 403
Response Message: Forbidden

我检查了代码,但找不到任何错误。我的意思是代码在 16 小时前还不错,但突然就不能正常工作了!


出于安全考虑,我无法向您展示我的安全密钥。但是,我确实编写了一个快速程序来查找 last.fm 会话密钥:http://github.com/thekarangoel/LastFMSessionKeyFinder 只需在 last.fm/api 注册,在程序中输入您的 API 密钥和密码,您将拥有测试 API 所需的一切打电话。

【问题讨论】:

  • 看起来您正在将 URL 参数写入实体正文。它们不应该是 URL 的一部分吗?
  • 我不这么认为,因为它是一个 POST 请求。

标签: java post http-headers httpurlconnection last.fm


【解决方案1】:

在生成 MD5 哈希时,您使用的是 titledescription 值的 URL 编码版本。 The documentation 并没有说要这样做。尝试使用非 URL 编码的值(尽管它们确实需要 UTF-8 编码)。 URL 编码只能用在实际的 URL 数据中(就是它的名字)。

试试这个:

private static void buildPlaylist() {

    String mood = "Happy";
    System.out.println("\nMood is " + mood + "\n\n");

    String title = mood + " " + new Date().getTime();
    String description = "For when you are " + mood + ". Created by MoodicPlayer.";

    MessageDigest md = MessageDigest.getInstance("MD5");

    String apiSig = "api_key" + key + "description" + description + "methodplaylist.createsk" + sessionKey + "title" + title + secret;
    md.update(apiSig.getBytes("UTF-8"));
    byte byteData[] = md.digest();
    //convert the byte to hex format
    StringBuffer sb = new StringBuffer(byteData.length*2);
    for (int i = 0; i < byteData.length; i++) {
        sb.append(String.format("%02X", byteData[i]));    
    }
    String hashedSig = sb.toString();

    // FOR DEBUGGING
    System.out.println("api_key = " + key);
    System.out.println("api_sig = " + hashedSig);
    System.out.println("session key = " + sessionKey);
    // FOR DEBUGGING

    String urlParameters = "method=playlist.create&api_key="+ key + "&api_sig=" + hashedSig + "&description=" + URLEncoder.encode(description, "UTF-8") + "&sk=" + sessionKey + "&title=" + URLEncoder.encode(title, "UTF-8");
    String request = "http://ws.audioscrobbler.com/2.0/";

    URL url = new URL(request);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();     
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setInstanceFollowRedirects(false); 
    connection.setRequestMethod("POST"); 
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
    connection.setRequestProperty("charset", "utf-8");
    connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
    connection.setUseCaches(false);

    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    wr.writeBytes(urlParameters);
    wr.flush();
    wr.close();

    ...
}

【讨论】:

  • 我试过了,也不管用。 API实际上说在生成签名时对参数进行编码:Ensure your parameters are utf8 encoded.
  • @user2059238 utf-8 编码不等于 URL 编码。这个答案是正确的,对我有用(除了代码中缺少的括号)
  • 我复制了您的答案,但仍然收到 403 Forbidden 错误。不确定它为什么/如何为您工作。
  • 我尝试了所有可能的方法,即使使用新的 API 密钥,但对我不起作用。这太他妈随意了。
  • 如果没有看到使用与您的代码相同的密钥的有效 HTTP 请求,很难诊断出实际失败的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-20
相关资源
最近更新 更多