使用Java解析Facebook signed_request会返回格式错误的JSON

时间:2011-09-13 17:54:39

标签: java json facebook

我正在尝试解析Java Servlet的doPost中的Facebook signed_request。我使用commons-codec-1.3的Base64解码签名的请求。 这是我在servlet的doPost

中使用的代码
String signedRequest = (String) req.getParameter("signed_request");
String payload = signedRequest.split("[.]", 2)[1];
payload = payload.replace("-", "+").replace("_", "/").trim();
String jsonString = new String(Base64.decodeBase64(payload.getBytes()));

当我在System.out jsonString时,它的格式不正确。有时它错过了JSON的结尾} 有时候它会在字符串的末尾错过"}

如何从Facebook获得正确的JSON响应?

5 个答案:

答案 0 :(得分:7)

Facebook正在使用Base64 for URL,您可能正在尝试使用标准Base64算法解码文本。 除其他外,URL变体不需要填充“=”。

  1. 您可以在代码中添加所需的字符(填充等)
  2. 你可以使用commons-codec 1.5(new Base64(true)),在那里他们添加了对这种编码的支持。

答案 1 :(得分:4)

Facebook正在向您发送“未填充”的Base64值(URL“标准”),这对于不期望它的Java解码器来说是个问题。当您要解码的Base64编码数据的长度不是4的倍数时,您可以告诉您有问题。

我使用此函数来修复值:

public static String padBase64(String b64) {
    String padding = "";
    // If you are a java developer, *this* is the critical bit.. FB expects
    // the base64 decode to do this padding for you (as the PHP one
    // apparently
    // does...
    switch (b64.length() % 4) {
    case 0:
        break;
    case 1:
        padding = "===";
        break;
    case 2:
        padding = "==";
        break;
    default:
        padding = "=";
    }
    return b64 + padding;

}

答案 2 :(得分:1)

我从未在Java中这样做过,所以我没有完整的答案,但事实上你有时会从字符串的末尾丢失一个,有时两个字符,这表明它可能是Base64填充的一个问题。您可能希望输出有效负载的值,并查看当它以'='结尾时,jsonString缺少'}',当有效负载以'=='结尾时,jsonString缺少''}'。如果这似乎是然后,在有效载荷末尾的等号的解释出现问题,这应该代表空位。

编辑:进一步反思我认为这是因为Facebook正在使用Base64 URL编码(不添加=作为填充字符)而不是常规Base64,而您的解码功能期望使用尾随=字符的常规Base64。

答案 3 :(得分:1)

我使用与此非常相似的代码升级到common-codec-1.5,但没有遇到此问题。您是否通过使用在线解码器确认有效负载确实是错误的?

答案 4 :(得分:0)

你好,2021 年。

其他答案已过时,因为在 Java 8 和更新版本中,您可以使用新的 Base64.getUrlDecoder()(而不是 getDecoder)解码 base64url 方案。

base64url 方案是主要 base64 方案的 URL 和文件名安全方言,并使用“-”代替“+”和“_”代替“/”(因为加号和斜线字符在 URL 中具有特殊含义)。此外,它不使用“=”字符作为字符串末尾的填充(0 到 4 个字符)。

以下是将 Java 中的 Facebook signed_request 参数解析为 Map 对象的方法:

public static Map<String, String> parseSignedRequest(HttpServletRequest httpReq, String facebookSecret) throws ServletException {
    String signedRequest = httpReq.getParameter("signed_request");
    String splitArray[]  = signedRequest.split("\\.", 2);
    String sigBase64     = splitArray[0];
    String payloadBase64 = splitArray[1];
    String payload       = new String(Base64.getUrlDecoder().decode(payloadBase64));

    try {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(facebookSecret.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secretKey);

        String sigExpected = Base64.getUrlEncoder().withoutPadding().encodeToString(sha256_HMAC.doFinal(payloadBase64.getBytes()));
        if (!sigBase64.equals(sigExpected)) {
            LOG.warn("sigBase64 = {}", sigBase64);
            LOG.warn("sigExpected = {}", sigExpected);
            throw new ServletException("Invalid sig = " + sigBase64);
        }
    } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException ex) {
        throw new ServletException("parseSignedRequest", ex);
    }

    // use Jetty JSON parsing or some other library
    return (Map<String, String>) JSON.parse(payload);
}

我使用了 Jetty JSON 解析器:

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-util</artifactId>
    <version>9.4.43.v20210629</version>
</dependency>

但是 Java 中有更多的库可用于解析 JSON。