XMPP 与 Java Asmack 库支持 X-FACEBOOK-PLATFORM
我正在尝试使用 Smack 库在 Android 上进行 Facebook 聊天。我已阅读 Facebook 的 Chat API,但我无法理解如何使用此库向 Facebook 进行身份验证。
谁能指出我如何实现这一目标?
更新:根据no.good.at.coding答案,我将此代码改编为Asmack库。一切正常,除了我收到登录响应:未授权。这是我使用的代码:
public class SASLXFacebookPlatformMechanism extends SASLMechanism
{
private static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String applicationSecret = "";
private String sessionKey = "";
/**
* Constructor.
*/
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication)
{
super(saslAuthentication);
}
@Override
protected void authenticate() throws IOException, XMPPException
{
getSASLAuthentication().send(new AuthMechanism(NAME, ""));
}
@Override
public void authenticate(String apiKeyAndSessionKey, String host,
String applicationSecret) throws IOException, XMPPException
{
if (apiKeyAndSessionKey == null || applicationSecret == null)
{
throw new IllegalArgumentException("Invalid parameters");
}
String[] keyArray = apiKeyAndSessionKey.split("\\|", 2);
if (keyArray.length < 2)
{
throw new IllegalArgumentException(
"API key or session key is not present");
}
this.apiKey = keyArray[0];
Log.d("API_KEY", apiKey);
this.applicationSecret = applicationSecret;
Log.d("SECRET_KEY", applicationSecret);
this.sessionKey = keyArray[1];
Log.d("SESSION_KEY", sessionKey);
this.authenticationId = sessionKey;
this.password = applicationSecret;
this.hostname = host;
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
this.sc =
Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,
this);
authenticate();
}
@Override
protected String getName()
{
return NAME;
}
@Override
public void challengeReceived(String challenge) throws IOException
{
byte[] response = null;
if (challenge != null)
{
String decodedChallenge = new String(Base64.decode(challenge));
Log.d("DECODED", decodedChallenge);
Map<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis() / 1000L;
String sig =
"api_key=" + apiKey + "call_id=" + callId + "method="
+ method + "nonce=" + nonce + "session_key="
+ sessionKey + "v=" + version + applicationSecret;
try
{
sig = md5(sig);
sig = sig.toUpperCase();
} catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
String composedResponse =
"api_key=" + URLEncoder.encode(apiKey, "utf-8")
+ "&call_id=" + callId + "&method="
+ URLEncoder.encode(method, "utf-8") + "&nonce="
+ URLEncoder.encode(nonce, "utf-8")
+ "&session_key="
+ URLEncoder.encode(sessionKey, "utf-8") + "&v="
+ URLEncoder.encode(version, "utf-8") + "&sig="
+ URLEncoder.encode(sig, "utf-8");
Log.d("COMPOSED", composedResponse);
response = composedResponse.getBytes("utf-8");
}
String authenticationText = "";
if (response != null)
{
authenticationText =
Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
// Send the authentication to the server
getSASLAuthentication().send(new Response(authenticationText));
}
private Map<String, String> getQueryMap(String query)
{
Map<String, String> map = new HashMap<String, String>();
String[] params = query.split("\\&");
for (String param : params)
{
String[] fields = param.split("=", 2);
map.put(fields[0], (fields.length > 1 ? fields[1] : null));
}
return map;
}
private String md5(String text) throws NoSuchAlgorithmException,
UnsupportedEncodingException
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes("utf-8"), 0, text.length());
return convertToHex(md.digest());
}
private String convertToHex(byte[] data)
{
StringBuilder buf = new StringBuilder();
int len = data.length;
for (int i = 0; i < len; i++)
{
int halfByte = (data[i] >>> 4) & 0xF;
int twoHalfs = 0;
do
{
if (0 <= halfByte && halfByte <= 9)
{
buf.append((char) ('0' + halfByte));
}
else
{
buf.append((char) ('a' + halfByte - 10));
}
halfByte = data[i] & 0xF;
} while (twoHalfs++ < 1);
}
return buf.toString();
}
}
这是通过发送和接收的消息与服务器进行通信:
PM SENT (1132418216): <stream:stream to="chat.facebook.com" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
PM RCV (1132418216): <?xml version="1.0"?><stream:stream id="C62D0F43" from="chat.facebook.com" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xml:lang="en"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>X-FACEBOOK-PLATFORM</mechanism><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features>
PM SENT (1132418216): <auth mechanism="X-FACEBOOK-PLATFORM" xmlns="urn:ietf:params:xml:ns:xmpp-sasl"></auth>
PM RCV (1132418216): <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9NzFGNkQ3Rjc5QkIyREJCQ0YxQTkwMzA0QTg3OTlBMzM=</challenge>
PM SENT (1132418216): <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">YXBpX2tleT0zMWYzYjg1ZjBjODYwNjQ3NThiZTZhOTQyNjVjZmNjMCZjYWxsX2lkPTEzMDA0NTYxMzUmbWV0aG9kPWF1dGgueG1wcF9sb2dpbiZub25jZT03MUY2RDdGNzlCQjJEQkJDRjFBOTAzMDRBODc5OUEzMyZzZXNzaW9uX2tleT0yNjUzMTg4ODNkYWJhOGRlOTRiYTk4ZDYtMTAwMDAwNTAyNjc2Nzc4JnY9MS4wJnNpZz04RkRDRjRGRTgzMENGOEQ3QjgwNjdERUQyOEE2RERFQw==</response>
PM RCV (1132418216): <failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>
如 开发者 Facebook 论坛,需要从应用的 Facebook 设置页面禁用“禁用已弃用的身份验证方法”设置。但是,即使这样做,我也无法登录。会话密钥是 OAuth 令牌的第二部分,格式为 AAA|BBB|CCC,我的意思是 BBB。
I'm trying to make a Facebook Chat on Android with the Smack library. I've read the Chat API from Facebook, but I cannot understand how I have to authenticate with Facebook using this library.
Can anyone point me how to accomplish this?
Update: According to the no.good.at.coding answer, I have this code adapted to the Asmack library. All works fine except I receive as response to the login: not-authorized. Here is the code I use:
public class SASLXFacebookPlatformMechanism extends SASLMechanism
{
private static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String applicationSecret = "";
private String sessionKey = "";
/**
* Constructor.
*/
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication)
{
super(saslAuthentication);
}
@Override
protected void authenticate() throws IOException, XMPPException
{
getSASLAuthentication().send(new AuthMechanism(NAME, ""));
}
@Override
public void authenticate(String apiKeyAndSessionKey, String host,
String applicationSecret) throws IOException, XMPPException
{
if (apiKeyAndSessionKey == null || applicationSecret == null)
{
throw new IllegalArgumentException("Invalid parameters");
}
String[] keyArray = apiKeyAndSessionKey.split("\\|", 2);
if (keyArray.length < 2)
{
throw new IllegalArgumentException(
"API key or session key is not present");
}
this.apiKey = keyArray[0];
Log.d("API_KEY", apiKey);
this.applicationSecret = applicationSecret;
Log.d("SECRET_KEY", applicationSecret);
this.sessionKey = keyArray[1];
Log.d("SESSION_KEY", sessionKey);
this.authenticationId = sessionKey;
this.password = applicationSecret;
this.hostname = host;
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
this.sc =
Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,
this);
authenticate();
}
@Override
protected String getName()
{
return NAME;
}
@Override
public void challengeReceived(String challenge) throws IOException
{
byte[] response = null;
if (challenge != null)
{
String decodedChallenge = new String(Base64.decode(challenge));
Log.d("DECODED", decodedChallenge);
Map<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis() / 1000L;
String sig =
"api_key=" + apiKey + "call_id=" + callId + "method="
+ method + "nonce=" + nonce + "session_key="
+ sessionKey + "v=" + version + applicationSecret;
try
{
sig = md5(sig);
sig = sig.toUpperCase();
} catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
String composedResponse =
"api_key=" + URLEncoder.encode(apiKey, "utf-8")
+ "&call_id=" + callId + "&method="
+ URLEncoder.encode(method, "utf-8") + "&nonce="
+ URLEncoder.encode(nonce, "utf-8")
+ "&session_key="
+ URLEncoder.encode(sessionKey, "utf-8") + "&v="
+ URLEncoder.encode(version, "utf-8") + "&sig="
+ URLEncoder.encode(sig, "utf-8");
Log.d("COMPOSED", composedResponse);
response = composedResponse.getBytes("utf-8");
}
String authenticationText = "";
if (response != null)
{
authenticationText =
Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
// Send the authentication to the server
getSASLAuthentication().send(new Response(authenticationText));
}
private Map<String, String> getQueryMap(String query)
{
Map<String, String> map = new HashMap<String, String>();
String[] params = query.split("\\&");
for (String param : params)
{
String[] fields = param.split("=", 2);
map.put(fields[0], (fields.length > 1 ? fields[1] : null));
}
return map;
}
private String md5(String text) throws NoSuchAlgorithmException,
UnsupportedEncodingException
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes("utf-8"), 0, text.length());
return convertToHex(md.digest());
}
private String convertToHex(byte[] data)
{
StringBuilder buf = new StringBuilder();
int len = data.length;
for (int i = 0; i < len; i++)
{
int halfByte = (data[i] >>> 4) & 0xF;
int twoHalfs = 0;
do
{
if (0 <= halfByte && halfByte <= 9)
{
buf.append((char) ('0' + halfByte));
}
else
{
buf.append((char) ('a' + halfByte - 10));
}
halfByte = data[i] & 0xF;
} while (twoHalfs++ < 1);
}
return buf.toString();
}
}
And this, is the communication with the server with the sent and received messages:
PM SENT (1132418216): <stream:stream to="chat.facebook.com" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
PM RCV (1132418216): <?xml version="1.0"?><stream:stream id="C62D0F43" from="chat.facebook.com" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xml:lang="en"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>X-FACEBOOK-PLATFORM</mechanism><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features>
PM SENT (1132418216): <auth mechanism="X-FACEBOOK-PLATFORM" xmlns="urn:ietf:params:xml:ns:xmpp-sasl"></auth>
PM RCV (1132418216): <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9NzFGNkQ3Rjc5QkIyREJCQ0YxQTkwMzA0QTg3OTlBMzM=</challenge>
PM SENT (1132418216): <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">YXBpX2tleT0zMWYzYjg1ZjBjODYwNjQ3NThiZTZhOTQyNjVjZmNjMCZjYWxsX2lkPTEzMDA0NTYxMzUmbWV0aG9kPWF1dGgueG1wcF9sb2dpbiZub25jZT03MUY2RDdGNzlCQjJEQkJDRjFBOTAzMDRBODc5OUEzMyZzZXNzaW9uX2tleT0yNjUzMTg4ODNkYWJhOGRlOTRiYTk4ZDYtMTAwMDAwNTAyNjc2Nzc4JnY9MS4wJnNpZz04RkRDRjRGRTgzMENGOEQ3QjgwNjdERUQyOEE2RERFQw==</response>
PM RCV (1132418216): <failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>
As read in the developers Facebook forum, it is needed to disable the "Disable Deprecated Auth Methods" setting from the Facebook settings page of your app. But, even doing that, I can't login. And the session key is the second part of the OAuth token in the form AAA|BBB|CCC, I mean, BBB.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
最后,感谢 no.good.at.coding 代码和 harism 的建议,我已经能够连接到 Facebook 聊天了。此代码是 Asmack 库(Android 的 Smack 端口)的机制。对于Smack库来说有必要使用no.good.at.coding机制。
SASLXFacebookPlatformMechanism.java:
使用方法:
apiKey 是 Facebook 的应用程序设置页面中给出的 API 密钥。 sessionKey 是访问令牌的第二部分。如果令牌采用以下形式:AAA|BBB|CCC,则 BBB 是会话密钥。 sessionSecret 是使用旧的 REST API 和 auth.promoteSession 方法获得的。要使用它,需要通过 Http 访问以下网址:
https://api.facebook.com /method/auth.promoteSession?access_token=yourAccessToken
尽管 Facebook 聊天文档说需要使用您的应用程序密钥,但只有当我使用返回 REST 方法的密钥时,我才能使其工作。要使该方法起作用,您必须在应用程序设置的“高级”选项卡中禁用禁用已弃用的身份验证方法选项。
Finally, thanks to the no.good.at.coding code and the suggestion of harism, I've been able to connect to the Facebook chat. This code is the Mechanism for the Asmack library (the Smack port for Android). For the Smack library is necessary to use the no.good.at.coding mechanism.
SASLXFacebookPlatformMechanism.java:
To use it:
apiKey is the API key given in the application settings page in Facebook. sessionKey is the second part of the access token. If the token is in this form, AAA|BBB|CCC, the BBB is the session key. sessionSecret is obtained using the old REST API with the method auth.promoteSession. To use it, it's needed to make a Http get to this url:
https://api.facebook.com/method/auth.promoteSession?access_token=yourAccessToken
Despite of the Facebook Chat documentation says that it's needed to use your application secret key, only when I used the key that returned that REST method I was able to make it works. To make that method works, you have to disable the Disable Deprecated Auth Methods option in the Advance tab in your application settings.
大约 6 个月前,我与 Smack(不是 asmack)一起使用了这个,所以我不确定它现在会如何维持,但这里是,希望它有帮助!
我在 Ignite Realtime Smack 论坛 上找到了 Facebook 的 X-FACEBOOK-PLATFORM 身份验证机制的实现有人从 fbgc 项目 获取它。您将在我链接到的答案中找到包含
SASLXFacebookPlatformMechanism.java
源的 ZIP。您可以按如下方式使用它:我是在没有 SDK 的服务器上执行此操作的。我不记得细节了(Facebook 文档不是很好),但从我的代码中可以看出,在让用户授权应用程序后,我收到来自 Facebook 的回调请求,其中包含
code
参数。我打开一个URLConnection
到https://graph.facebook.com/oauth/access_token?client_id=&redirect_uri=http://myserver/context/path/& ;client_secret=&code=
。响应应该是访问令牌,其中会话 ID 是
|
之后的部分 - 格式为XXX|
。I'd used this about 6 months ago with Smack (not asmack) so I'm not sure how it'll hold up now but here goes, hope it helps!
I found an implementation of Facebook's X-FACEBOOK-PLATFORM authentication mechanism on the Ignite Realtime Smack forum where someone got it from the fbgc project. You'll find the a ZIP with the
SASLXFacebookPlatformMechanism.java
source in the answer I linked to. You can use it as follows:I was doing this on the server without an SDK. I don't remember the details (and the Facebook documentation isn't very good) but from what I can tell from my code, after getting the user to authorize the app, I get a callback request from Facebook with a
code
parameter. I open aURLConnection
tohttps://graph.facebook.com/oauth/access_token?client_id=<app_id>&redirect_uri=http://myserver/context/path/&client_secret=<app_secret>&code=<code>
. The response should be the access token where the session id is the part after the|
- something of the formXXX|<sessionId>
.这是我已成功用于身份验证的代码。也许这会有所帮助,尽管这与 Smack 没有任何关系。您可以从 FB 收到的访问令牌中获取 sessionKey,为了获取 sessionSecret 我一直在使用旧的 REST API;
http://developers.facebook.com/docs/reference/rest/auth。推广会话/
Here's code I've been using successfully for authentication. Maybe this helps even though this is not related to Smack in any way. You can get sessionKey from access token received from FB, and for getting sessionSecret I've been using oldish REST API;
http://developers.facebook.com/docs/reference/rest/auth.promoteSession/
我很抱歉做出新的答案,但我必须包含新代码@YShinkarev 抱歉迟到了
通过修改@Adrian答案以使challengeReceived我们可以使用APIKey和accessToken我修改的是composedResponse
I'm sorry to make new answer but I had to include the new code @YShinkarev sorry for being late
By modifying @Adrian answer to make challengeReceived we can use APIKey and accessToken all I modified was the composedResponse
你想让我做什么?
如果您只想登录 FB 聊天,您可以像任何其他 XMPP 服务器一样连接到 FB。
我会查看并使用 Chat API 中的“使用用户名/密码进行身份验证”,该 API 由 Smack 支持。除非我想编写一个 Facebook 应用程序。然后我会尝试使用“使用 Facebook 平台进行身份验证”登录。
因此,只需使用 Smack 连接到 FB 聊天,就像使用普通的 Jabber 客户端一样。
What do you want to do?
If you just want to login on FB chat, you connect to FB just like any other XMPP server.
I would look at and use "Authenticating with Username/Password" from Chat API, wich is supported by Smack. Unless I would like to write an FaceBook-application. Then I would try to login in with "Authenticating with Facebook Platform".
So, just use Smack to connect to FB chat as you would do with your ordinary Jabber client.