我正在尝试解析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响应?
答案 0 :(得分:7)
Facebook正在使用Base64 for URL,您可能正在尝试使用标准Base64算法解码文本。 除其他外,URL变体不需要填充“=”。
答案 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。