无法使用asmack库登录google talk for android

时间:2014-09-15 11:15:22

标签: android oauth-2.0 smack asmack google-talk

我一直试图使用asmack库登录google talk但没有成功。我真的不知道幕后发生了什么,只是从这里和那里收集了一些代码片段。这就是我目前用于Android活动的内容:

public class MainActivity extends Activity {                                                          

    public static final String HOST = "talk.google.com";                                              
    public static final int PORT = 5222;                                                              
    public static final String SERVICE = "gmail.com";                                                 
    public static final String USER = "user@gmail.com"; 
    public static final String PASSWORD = "password"; 

    @Override                                                                                         
    public void onCreate(Bundle savedInstanceState) {                                                 
        super.onCreate(savedInstanceState);                                                           
        setContentView(R.layout.main);                                                                
        Context context = getApplicationContext();                                                    
        SmackAndroid asmk = SmackAndroid.init(context);                                               
        SASLAuthentication.registerSASLMechanism("X-OAUTH2", SASLGoogleOAuth2Mechanism.class);        
        SASLAuthentication.supportSASLMechanism("X-OAUTH2", 0);                                       
        ConnectionConfiguration connConfig = new ConnectionConfiguration(HOST, PORT, SERVICE);        
        connConfig.setSecurityMode(SecurityMode.enabled);                                             
        connConfig.setReconnectionAllowed(true);                                                      

        XMPPTCPConnection connection = new XMPPTCPConnection(connConfig);                             
        try {                                                                                         
            connection.connect();                                                                     
            try {                                                                                     
                connection.login(USER, PASSWORD);                         
            } catch (XMPPException ex) {                                                              
                Log.w("XMPPChatDemoActivity", "Failed to log in");                                    
                Log.w("XMPPChatDemoActivity", ex.getMessage());                 
            }                                                                                         
        } catch (...) {                                                                  
           ...
        }
    }
}

这就是SASLMechanism:

public class SASLGoogleOAuth2Mechanism extends SASLMechanism {

    private static final Logger log = Logger.getLogger("XMPPChatDemoActivity");
    public static final String NAME = "X-OAUTH2";

    public SASLGoogleOAuth2Mechanism(SASLAuthentication saslAuthentication) {
        super(saslAuthentication);
        log.info("Creating SASL mechanism for GTalk (X-OAUTH2)");
    }

    @Override
    public void authenticate(String username, String host, String serviceName, String password) throws IOException, SaslException, NotConnectedException {
        this.authenticationId = username;
        this.hostname = host;
        this.password = password;

        String[] mechanisms = { "PLAIN" };
        Map<String, String> props = new HashMap<String, String>();
        this.sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
        log.info("sc " + sc);
        authenticate();
    }

    @Override
    public void authenticate(String host, CallbackHandler cbh) throws IOException, SaslException, NotConnectedException {
        String[] mechanisms = { "PLAIN" };
        Map<String, String> props = new HashMap<String, String>();

        sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
        authenticate();
    }
    @Override
    protected void authenticate() throws IOException, SaslException, NotConnectedException {
        String authenticationText = null;

        try { 
            if (sc.hasInitialResponse()) {
                byte[] response = sc.evaluateChallenge(new byte[0]);
                authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
            }
        } catch (SaslException e) {
            throw new SaslException("SASL authentication failed", e);
        }

        // Send the authentication to the server
        getSASLAuthentication().send(new GoogleOAuthMechanism(authenticationText));
    }
    @Override
    protected String getName() {
        return NAME;
    }
    /**
     * Initiating SASL authentication by select a mechanism.
     */
    public static class GoogleOAuthMechanism extends Packet {
        private final String authenticationText;

        /**
         * Create a GoogleOAuthMechanism.
         *
         * @param authenticationText the authentification token
         *
         */
        public GoogleOAuthMechanism(final String authenticationText) {
            this.authenticationText = authenticationText;
        }

        @Override
        public String toXML() {

            StringBuilder stanza = new StringBuilder();
            stanza.append("<auth mechanism=\"").append(NAME);
            stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
                    + "auth:service=\"oauth2\" "
                    + "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">");
            if (authenticationText != null
                    && authenticationText.trim().length() > 0) {
                stanza.append(authenticationText);
            }
            stanza.append("</auth>");
            return stanza.toString();
        }
    }
}

代码没问题,我没有任何异常,但我收到<not-authorized>回复。用户名和密码是正确的。我找不到这个库的任何参考代码。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

经过几天的努力并尝试了我在互联网上找到的所有可以想象的片段组合后,我遇到了一个我很乐意与社区分享的解决方案。

我们应该使用来自Google 的身份验证令牌,而不是将密码传递给XMPPTCPConnection.login()方法。我发现a post解释了生成这种令牌的方法。我的similar question存在,但它也使用X-GOOGLE-TOKEN机制进行身份验证,这与使用X-OAUTH2机制进行身份验证的方法不同。此外,我发现的所有其他与使用OAUTH2进行谷歌对话身份验证问题相关的帖子都很旧。我正在使用 smack 4.0.4 的asmack版本。

因此,问题中显示的代码所需的唯一修改是:

AccountManager am = AccountManager.get(this);
Account accounts[] = am.getAccountsByType("com.google");
conn.login(USER, amf.blockingGetAuthToken(accounts[0], GOOGLE_TOKEN_TYPE, true));

如您所见,我使用存储在设备上的帐户来证明解决方案,但您可以通过其他方式生成令牌,如上所述。

最后,当我通过反复试验找到解决方案时,我会很感激任何人解释实际发生的事情或我可能给出的任何错误信息,因此这个答案可以进一步改进。