带有 Java Asmack 库的 XMPP 支持 X-FACEBOOK-PLATFORM

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5317329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:36:29  来源:igfitidea点击:

XMPP with Java Asmack library supporting X-FACEBOOK-PLATFORM

javaandroidfacebookxmppsmack

提问by Adrian

I'm trying to make a Facebook Chat on Android with the Smack library. I've read the Chat APIfrom Facebook, but I cannot understand how I have to authenticate with Facebook using this library.

我正在尝试使用 Smack 库在 Android 上进行 Facebook 聊天。我已经从 Facebook阅读了Chat API,但我无法理解如何使用此库向 Facebook 进行身份验证。

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:

更新:根据 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();
    }
}

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.

正如在开发人员 Facebook 论坛中所读到的,需要从应用程序的 Facebook 设置页面禁用“禁用已弃用的身份验证方法”设置。但是,即使这样做,我也无法登录。会话密钥是 OAuth 令牌的第二部分,格式为 AAA|BBB|CCC,我的意思是 BBB。

回答by Adrian

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.

最后,感谢 no.good.at.coding 代码和 harism 的建议,我已经能够连接到 Facebook 聊天。此代码是 Asmack 库(Android 的 Smack 端口)的机制。对于 Smack 库,必须使用 no.good.at.coding 机制。

SASLXFacebookPlatformMechanism.java:

SASLXFacebookPlatformMechanism.java:

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
import org.apache.harmony.javax.security.sasl.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;

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];
        this.applicationSecret = applicationSecret;
        this.sessionKey = keyArray[1];

        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
    public void authenticate(String username, String host, CallbackHandler cbh)
            throws IOException, XMPPException
    {
        String[] mechanisms = { "DIGEST-MD5" };
        Map<String, String> props = new HashMap<String, String>();
        this.sc =
                Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,
                        cbh);
        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));
            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();

            String sig =
                    "api_key=" + apiKey + "call_id=" + callId + "method="
                            + method + "nonce=" + nonce + "session_key="
                            + sessionKey + "v=" + version + applicationSecret;

            try
            {
                sig = md5(sig);
            } 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");

            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();
    }
}

To use it:

要使用它:

ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
config.setSASLAuthenticationEnabled(true);
XMPPConnection xmpp = new XMPPConnection(config);
try
{
    SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
    SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
    xmpp.connect();
    xmpp.login(apiKey + "|" + sessionKey, sessionSecret, "Application");
} catch (XMPPException e)
{
    xmpp.disconnect();
    e.printStackTrace();
}

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:

apiKey 是 Facebook 应用程序设置页面中给出的 API 密钥。sessionKey 是访问令牌的第二部分。如果令牌采用这种形式,AAA|BBB|CCC,则 BBB 是会话密钥。sessionSecret 是使用旧的 REST API 和方法 auth.promoteSession 获得的。要使用它,需要使 Http 访问此 url:

https://api.facebook.com/method/auth.promoteSession?access_token=yourAccessToken

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 Methodsoption in the Advance tab in your application settings.

尽管 Facebook 聊天文档说需要使用您的应用程序密钥,但只有当我使用返回该 REST 方法的密钥时,我才能使其正常工作。要使该方法有效,您必须在应用程序设置的“高级”选项卡中禁用“禁用已弃用的身份验证方法”选项。

回答by no.good.at.coding

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!

大约 6 个月前,我在 Smack(而不是 asmack)上使用过它,所以我不确定它现在会如何,但在这里,希望它有所帮助!

I found an implementation of Facebook's X-FACEBOOK-PLATFORM authentication mechanism on the Ignite Realtime Smack forumwhere someone got it from the fbgc project. You'll find the a ZIP with the SASLXFacebookPlatformMechanism.javasource in the answer I linked to. You can use it as follows:

我在Ignite Realtime Smack 论坛上找到了 Facebook 的 X-FACEBOOK-PLATFORM 身份验证机制的实现,有人从fbgc 项目中获得了它。您会SASLXFacebookPlatformMechanism.java在我链接到的答案中找到带有源代码的 ZIP 。您可以按如下方式使用它:

public void login() throws XMPPException
{
    SASLAuthentication.registerSASLMechanism(SASLXFacebookPlatformMechanism.NAME,
            SASLXFacebookPlatformMechanism.class);
    SASLAuthentication.supportSASLMechanism(SASLXFacebookPlatformMechanism.NAME, 0);

    ConnectionConfiguration connConfig = new ConnectionConfiguration(host, port);

    XMPPConnection connection = new XMPPConnection(connConfig);
    connection.connect();
    log.info("XMPP client connected");

    connection.login(Utils.FB_APP_ID + "|" + this.user.sessionId, Utils.FB_APP_SECRET, "app_name");
    log.info("XMPP client logged in");
}

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 codeparameter. I open a URLConnectionto https://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 form XXX|<sessionId>.

我在没有 SDK 的服务器上执行此操作。我不记得细节(而且 Facebook 文档不是很好),但从我的代码中可以看出,在让用户授权应用程序后,我收到了来自 Facebook 的带有code参数的回调请求。我打开URLConnectionhttps://graph.facebook.com/oauth/access_token?client_id=<app_id>&redirect_uri=http://myserver/context/path/&client_secret=<app_secret>&code=<code>。响应应该是访问令牌,其中会话 id 是|表单的-something 之后的部分XXX|<sessionId>

回答by harism

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;

这是我成功用于身份验证的代码。也许这会有所帮助,即使这与 Smack 没有任何关系。您可以从从 FB 收到的访问令牌中获取 sessionKey,为了获取 sessionSecret,我一直在使用旧式 REST API;

http://developers.facebook.com/docs/reference/rest/auth.promoteSession/

http://developers.facebook.com/docs/reference/rest/auth.promoteSession/

private final void processChallenge(XmlPullParser parser, Writer writer,
        String sessionKey, String sessionSecret) throws IOException,
        NoSuchAlgorithmException, XmlPullParserException {

    parser.require(XmlPullParser.START_TAG, null, "challenge");
    String challenge = new String(Base64.decode(parser.nextText(),
            Base64.DEFAULT));

    String params[] = challenge.split("&");
    HashMap<String, String> paramMap = new HashMap<String, String>();
    for (int i = 0; i < params.length; ++i) {
        String p[] = params[i].split("=");
        p[0] = URLDecoder.decode(p[0]);
        p[1] = URLDecoder.decode(p[1]);
        paramMap.put(p[0], p[1]);
    }

    String api_key = "YOUR_API_KEY";
    String call_id = "" + System.currentTimeMillis();
    String method = paramMap.get("method");
    String nonce = paramMap.get("nonce");
    String v = "1.0";

    StringBuffer sigBuffer = new StringBuffer();
    sigBuffer.append("api_key=" + api_key);
    sigBuffer.append("call_id=" + call_id);
    sigBuffer.append("method=" + method);
    sigBuffer.append("nonce=" + nonce);
    sigBuffer.append("session_key=" + sessionKey);
    sigBuffer.append("v=" + v);
    sigBuffer.append(sessionSecret);

    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(sigBuffer.toString().getBytes());
    byte[] digest = md.digest();

    StringBuffer sig = new StringBuffer();
    for (int i = 0; i < digest.length; ++i) {
        sig.append(Integer.toHexString(0xFF & digest[i]));
    }

    StringBuffer response = new StringBuffer();
    response.append("api_key=" + URLEncoder.encode(api_key));
    response.append("&call_id=" + URLEncoder.encode(call_id));
    response.append("&method=" + URLEncoder.encode(method));
    response.append("&nonce=" + URLEncoder.encode(nonce));
    response.append("&session_key=" + URLEncoder.encode(sessionKey));
    response.append("&v=" + URLEncoder.encode(v));
    response.append("&sig=" + URLEncoder.encode(sig.toString()));

    StringBuilder out = new StringBuilder();
    out.append("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>");
    out.append(Base64.encodeToString(response.toString().getBytes(),
            Base64.NO_WRAP));
    out.append("</response>");

    writer.write(out.toString());
    writer.flush();
}

回答by Amal

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

我很抱歉做出新的答案,但我必须包含新代码 @YShinkarev 抱歉迟到
通过修改@Adrian 答案使challengeReceived 我们可以使用API​​Key 和accessToken 我修改的只是composerResponse

@Override
public void challengeReceived(String challenge) throws IOException {
    byte[] response = null;

    if (challenge != null) {
        String decodedChallenge = new String(Base64.decode(challenge));
        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();

        String composedResponse = "api_key="
                + URLEncoder.encode(apiKey, "utf-8") + "&call_id=" + callId
                + "&method=" + URLEncoder.encode(method, "utf-8")
                + "&nonce=" + URLEncoder.encode(nonce, "utf-8")
                + "&access_token="
                + URLEncoder.encode(access_token, "utf-8") + "&v="
                + URLEncoder.encode(version, "utf-8");

        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));
}

回答by Anders

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.

如果您只想登录 FB 聊天,您可以像连接任何其他 XMPP 服务器一样连接到 FB。

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".

我会查看并使用来自 Chat API 的“使用用户名/密码进行身份验证”,这由 Smack 支持。除非我想写一个 Facebook 应用程序。然后我会尝试使用“使用 Facebook 平台进行身份验证”登录。

So, just use Smack to connect to FB chat as you would do with your ordinary Jabber client.

因此,只需像使用普通 Jabber 客户端一样使用 Smack 连接到 FB 聊天即可。

  1. For the username, use your Facebook username. (see http://www.facebook.com/username/)
  2. For the domain, use: chat.facebook.com
  3. For the password, use your Facebook password
  4. Turn offSSL and TSL
  5. Set connect port to: 5222(which is the default for XMPP)
  6. Set connect server to chat.facebook.com
  1. 对于用户名,请使用您的 Facebook用户名。(见http://www.facebook.com/username/
  2. 对于域,请使用:chat.facebook.com
  3. 对于密码,请使用您的 Facebook密码
  4. 关闭关闭SSL和TSL
  5. 将连接端口设置为:5222(这是 XMPP 的默认值)
  6. 将连接服务器设置为chat.facebook.com