Smack 开源库学习总结(三)登录鉴权

  • Smack 开源库学习总结(三)登录鉴权
    • 1 login()流程梳理
    • 2 鉴权消息流分析

Smack 开源库学习总结(三)登录鉴权

本篇文章主要学习 XMPPTCPConnection中函数login()的具体实现的功能以及举例说明SASL算法处理流程。

1 login()流程梳理

connection.login();

login()在AbstractXMPPConnection.java中实现,其只是登录鉴权的总入口而已。

    public synchronized void login() throws XMPPException, SmackException, IOException, InterruptedException {
        // The previously used username, password and resource take over precedence over the
        // ones from the connection configuration
        CharSequence username = usedUsername != null ? usedUsername : config.getUsername();
        String password = usedPassword != null ? usedPassword : config.getPassword();
        Resourcepart resource = usedResource != null ? usedResource : config.getResource();
        login(username, password, resource);
    }

实际登录鉴权的动作是由抽象函数loginInternal()处理,在XMPPTCPConnection中实现了loginInternal()具体动作,在该函数中调用saslAuthentication.authenticate()进行鉴权。

    protected synchronized void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
                    SmackException, IOException, InterruptedException {
        // Authenticate using SASL
        SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null;
        saslAuthentication.authenticate(username, password, config.getAuthzid(), sslSession);
        
        ......(下面代码省略)
        }

首先,saslAuthentication.authenticate()中调用selectMechanism()获取服务器支持的SASL算法currentMechanism对象;
然后,通过currentMechanism.authenticate()进行鉴权,在authenticate()中通过函数getAuthenticationText()获取currentMechanism对象的鉴权内容,并发送给服务器;
注意:
(1) currentMechanism对象是具体的 SASL 算法对象;
(2) 在authenticate()中发送给服务器的鉴权信息是经过BASE64加密处理;

    private void authenticate() throws SmackException, NotConnectedException, InterruptedException {
        byte[] authenticationBytes = getAuthenticationText();
        String authenticationText;
        // Some SASL mechanisms do return an empty array (e.g. EXTERNAL from javax), so check that
        // the array is not-empty. Mechanisms are allowed to return either 'null' or an empty array
        // if there is no authentication text.
        if (authenticationBytes != null && authenticationBytes.length > 0) {
            authenticationText = Base64.encodeToString(authenticationBytes);
        } else {
            // RFC6120 6.4.2 "If the initiating entity needs to send a zero-length initial response,
            // it MUST transmit the response as a single equals sign character ("="), which
            // indicates that the response is present but contains no data."
            authenticationText = "=";
        }
        // Send the authentication to the server
        connection.sendNonza(new AuthMechanism(getName(), authenticationText));
    }

服务器鉴权成功后,回调authenticated(),表明已经鉴权成功。

2 鉴权消息流分析

上面重点讲解了login()具体做了哪些事情,其中最重要的是鉴权的整个流程,下面以实际案例讲解鉴权的过程。
首先,服务器下发支持的SASL算法数据流。

<stream:features>
    <mechanisms
        xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
        <mechanism>DIGEST-MD5mechanism>
    mechanisms>
stream:features>

然后,在parsePackets() 中调用数据流类型"features",找到对应的case,执行相应的解析工作,在parseFeatures()中解析出SASL算法类型为"DIGEST-MD5",通过函数addStreamFeature()将算法类型添加到streamFeatures中。

private void parsePackets() {
            try {
                initialOpenStreamSend.checkIfSuccessOrWait();
                int eventType = parser.getEventType();
                while (!done) {
                    switch (eventType) {
                    case XmlPullParser.START_TAG:
                        final String name = parser.getName();
                        switch (name) {
                        case Message.ELEMENT:
                        case IQ.IQ_ELEMENT:
                        case Presence.ELEMENT:
                            try {
                                parseAndProcessStanza(parser);
                            } finally {
                                clientHandledStanzasCount = SMUtils.incrementHeight(clientHandledStanzasCount);
                            }
                            break;
                        case "stream":
                            // We found an opening stream.
                            if ("jabber:client".equals(parser.getNamespace(null))) {
                                streamId = parser.getAttributeValue("", "id");
                                String reportedServerDomain = parser.getAttributeValue("", "from");
                                assert (config.getXMPPServiceDomain().equals(reportedServerDomain));
                            }
                            break;
                        case "error":
                            StreamError streamError = PacketParserUtils.parseStreamError(parser);
                            saslFeatureReceived.reportFailure(new StreamErrorException(streamError));
                            // Mark the tlsHandled sync point as success, we will use the saslFeatureReceived sync
                            // point to report the error, which is checked immediately after tlsHandled in
                            // connectInternal().
                            tlsHandled.reportSuccess();
                            throw new StreamErrorException(streamError);
                        case "features":
                            parseFeatures(parser);
                            break;
                            .......(下面代码省略)
                        }
                    }
                } 
            }
        }    

在login()登录鉴权的过程中,会调用函数selectMechanism()获取服务器支持的SASL算法currentMechanism对象(上面有讲过),函数selectMechanism()会去streamFeatures中找到服务器下发的SASL算法类型。

你可能感兴趣的:(xmpp)