openfire用户登陆验证流程

一:openfire登陆和退出报文源码解读:

openfire提供给开发者的接口是AuthProvider,通过实现该接口的

void authenticate(String var1, String var2) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException;
    String getPassword(String var1) throws UserNotFoundException, UnsupportedOperationException;
    void setPassword(String var1, String var2) throws UserNotFoundException, UnsupportedOperationException;

    boolean supportsPasswordRetrieval();

    boolean isScramSupported();

    String getSalt(String var1) throws UnsupportedOperationException, UserNotFoundException;

    int getIterations(String var1) throws UnsupportedOperationException, UserNotFoundException;

    String getServerKey(String var1) throws UnsupportedOperationException, UserNotFoundException;

    String getStoredKey(String var1) throws UnsupportedOperationException, UserNotFoundException;

openfire提供了DefaultAuthProvider,CrowdAuthProvider等实现方式。
以上是开发的接口。
除了上面的以外,我们需要知道openfire是如何进行验证的,在什么位置验证的,是否还有其它的接口给我们使用,比如监听器。

首先看我们通过调试寻找到相关的类和相关的代码。

 in StanzaHandler.java
    public void process(String stanza, XMPPPacketReader reader) throws Exception {
            boolean initialStream = stanza.startsWith("<stream:stream") || stanza.startsWith("<flash:stream");
            if (!sessionCreated || initialStream) {
    //处理报文<stream:stream xmlns='jabber:client' to='localhost' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='milo@localhost' xml:lang='en'>
                if (!initialStream) {
                    // Allow requests for flash socket policy files directly on the client listener port
                    if (stanza.startsWith("<policy-file-request/>")) {
                        String crossDomainText = FlashCrossDomainServlet.CROSS_DOMAIN_TEXT +
                                XMPPServer.getInstance().getConnectionManager().getClientListenerPort() +
                                FlashCrossDomainServlet.CROSS_DOMAIN_END_TEXT + '\0';
                        connection.deliverRawText(crossDomainText);
                        return;
                    }
                    else {
                        // Ignore <?xml version="1.0"?>
                        return;
                    }
                }
                // Found an stream:stream tag...
                if (!sessionCreated) {
                    sessionCreated = true;
                    MXParser parser = reader.getXPPParser();
                    parser.setInput(new StringReader(stanza));
                    createSession(parser);
                }
                else if (startedTLS) {
                    //处理<stream:stream xmlns='jabber:client' to='localhost' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='zeng@localhost' xml:lang='en'>,使用tls协议
                    startedTLS = false;
                    tlsNegotiated();
                }
                else if (startedSASL && saslStatus == SASLAuthentication.Status.authenticated) {
                    startedSASL = false;
                    saslSuccessful();
                }
                else if (waitingCompressionACK) {
                    waitingCompressionACK = false;
                    compressionSuccessful();
                }
                return;
            }
            // Verify if end of stream was requested,这儿处理</stream:stream>的登出报文
            if (stanza.equals("</stream:stream>")) {
                if (session != null) {
                    session.getStreamManager().formalClose();
                    Log.debug( "Closing session as an end-of-stream was received: {}", session );
                    session.close();
                }
                return;
            }
            // Ignore <?xml version="1.0"?> stanzas sent by clients
            if (stanza.startsWith("<?xml")) {
                return;
            }
            // Create DOM object from received stanza
            Element doc = reader.read(new StringReader(stanza)).getRootElement();
            if (doc == null) {
                // No document found.
                return;
            }
            String tag = doc.getName();
            if ("starttls".equals(tag)) {
          //处理<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>报文,约定对于账户密码使用TLS加密
                // Negotiate TLS
                if (negotiateTLS()) {
                    startedTLS = true;
                }
                else {
                    connection.close();
                    session = null;
                }
            }
            else if ("auth".equals(tag)) {
                //这儿处理<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='SCRAM-SHA-1'>biwsbj16ZW5nLHI9XyJ4fW1PPVotNFIvb1htPWgubm5NYU85bnw5KUNiRCU=</auth>报文
                // User is trying to authenticate using SASL
                startedSASL = true;
                // Process authentication stanza
                saslStatus = SASLAuthentication.handle(session, doc);
            } else if (startedSASL && "response".equals(tag) || "abort".equals(tag)) {
            //这儿处理<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Yz1iaXdzLHI9XyJ4fW1PPVotNFIvb1htPWgubm5NYU85bnw5KUNiRCU1N2VkMzA0ZC0yYjc1LTQ2NjAtYThlYi0wYWMwMmU3NWVmYTkscD1kNGxCcDFPbnBJbEI4cWRveE9Bbld4elZlMms9</response>
                // User is responding to SASL challenge. Process response
                saslStatus = SASLAuthentication.handle(session, doc);
            }
            else if ("compress".equals(tag)) {
                // Client is trying to initiate compression
                if (compressClient(doc)) {
                    // Compression was successful so open a new stream and offer
                    // resource binding and session establishment (to client sessions only)
                    waitingCompressionACK = true;
                }
            } else if (isStreamManagementStanza(doc)) {
                session.getStreamManager().process( doc );
            }
            else {
                process(doc);
            }
        }

相关执行流程StanzaHandler.process()->SASLAuthentication.handle()->ScramSha1SaslServer.getSalt()->AuthFactory.getSalt()->DefaultAuthProvider.getSalt()

登入的报文:

<stream:stream xmlns='jabber:client' to='localhost' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='milo@localhost' xml:lang='en'>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>
<stream:stream xmlns='jabber:client' to='localhost' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='milo@localhost' xml:lang='en'>
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='SCRAM-SHA-1'>biwsbj1taWxvLHI9WVR4dlY9TW1ueUhQLSRWUjpQKUxuaSdFPyRWNEw6ejo=</auth>
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Yz1iaXdzLHI9WVR4dlY9TW1ueUhQLSRWUjpQKUxuaSdFPyRWNEw6ejozMDU3MTliZi1mNDFmLTQ3NTctODQyZC02NWJmYWZhZDMxZjcscD1HMjJ4VmZiaGdZZWZLcTlqcUxpbU5EQjlGeTA9</response>

登出报文如下:

<presence id='6Y86M-86' type='unavailable'></presence></stream:stream>

现在来session相关的,这个是经常使用的,我们常常需要知道当前有那些客户端连接着就是通过session完成的,还有报文的发送和接受。

在这里插入图片描述

private static final Logger Log = LoggerFactory.getLogger(LocalClientSession.class);

    private static final String ETHERX_NAMESPACE = "http://etherx.jabber.org/streams";
    private static final String FLASH_NAMESPACE = "http://www.jabber.com/streams/flash";

    /**
     * Keep the list of IP address that are allowed to connect to the server.
     *
     * If the list is  empty then anyone is allowed to connect to the server, unless the IP is on the blacklist (which
     * always takes precedence over the whitelist).
     *
     * Note: the values in this list can be hostnames, IP addresses or IP ranges (with wildcards).
     */
    private static Set<String> allowedIPs = new HashSet<>();
    private static Set<String> allowedAnonymIPs = new HashSet<>();

    /**
     * Similar to {@link #allowedIPs}, but used for blacklisting rather than whitelisting.
     */
    private static Set<String> blockedIPs = new HashSet<>();

    private boolean messageCarbonsEnabled;

    /**
     * The authentication token for this session.
     */
    protected AuthToken authToken;

    /**
     * Flag indicating if this session has been initialized yet (upon first available transition).
     */
    private boolean initialized;

    /**
     * Flag that indicates if the session was available ever.
     */
    private boolean wasAvailable = false;

    /**
     * Flag indicating if the user requested to not receive offline messages when sending
     * an available presence. The user may send a disco request with node
     * "http://jabber.org/protocol/offline" so that no offline messages are sent to the
     * user when he becomes online. If the user is connected from many resources then
     * if one of the sessions stopped the flooding then no session should flood the user.
     */
    private boolean offlineFloodStopped = false;

    private Presence presence = null;

    private int conflictCount = 0;

    /**
     * Privacy list that overrides the default privacy list. This list affects only this
     * session and only for the duration of the session.
     */
    private String activeList;
    /**
     * Default privacy list used for the session's user. This list is processed if there
     * is no active list set for the session.
     */
    private String defaultList;

sessionManager.java,实际执行创建的位置

/**
     * Counter of user connections. A connection is counted just after it was created and not
     * after the user became available. This counter only considers sessions local to this JVM.
     * That means that when running inside of a cluster you will need to add up this counter
     * for each cluster node.
     */
    private final AtomicInteger connectionsCounter = new AtomicInteger(0);

    /**
     * Cache (unlimited, never expire) that holds information about client sessions (as soon as
     * a resource has been bound). The cache is used by Remote sessions to avoid generating big
     * number of remote calls.
     * Key: full JID, Value: ClientSessionInfo
     */
    private Cache<String, ClientSessionInfo> sessionInfoCache;

    /**
     * Cache (unlimited, never expire) that holds external component sessions.
     * Key: component address, Value: nodeID
     */
    private Cache<String, byte[]> componentSessionsCache;

    /**
     * Cache (unlimited, never expire) that holds sessions of connection managers. For each
     * socket connection of the CM to the server there is going to be an entry in the cache.
     * Key: full address of the CM that identifies the socket, Value: nodeID
     */
    private Cache<String, byte[]> multiplexerSessionsCache;

    /**
     * Cache (unlimited, never expire) that holds incoming sessions of remote servers.
     * Key: stream ID that identifies the socket/session, Value: nodeID
     */
    private Cache<StreamID, byte[]> incomingServerSessionsCache;
    /**
     * Cache (unlimited, never expire) that holds list of incoming sessions
     * originated from the same remote server (domain/subdomain). For instance, jabber.org
     * may have 2 connections to the server running in jivesoftware.com (one socket to
     * jivesoftware.com and the other socket to conference.jivesoftware.com).
     * Key: remote hostname (domain/subdomain), Value: list of stream IDs that identify each socket.
     */
    private Cache<String, List<StreamID>> hostnameSessionsCache;

    /**
     * Cache (unlimited, never expire) that holds domains, subdomains and virtual
     * hostnames of the remote server that were validated with this server for each
     * incoming server session.
     * Key: stream ID, Value: Domains and subdomains of the remote server that were
     * validated with this server.<p>
     *
     * This same information is stored in {@link LocalIncomingServerSession} but the
     * reason for this duplication is that when running in a cluster other nodes
     * will have access to this clustered cache even in the case of this node going
     * down.
     */
    private Cache<StreamID, Set<String>> validatedDomainsCache;
	//这儿的监听器都是只针对监听connection关闭的。
    private ClientSessionListener clientSessionListener = new ClientSessionListener();
    private ComponentSessionListener componentSessionListener = new ComponentSessionListener();
    private IncomingServerSessionListener incomingServerListener = new IncomingServerSessionListener();
    private OutgoingServerSessionListener outgoingServerListener = new OutgoingServerSessionListener();
    private ConnectionMultiplexerSessionListener multiplexerSessionListener = new ConnectionMultiplexerSessionListener();
    
/**
     * Creates a new <tt>ClientSession</tt> with the specified streamID.
     *
     * @param conn the connection to create the session from.
     * @param id the streamID to use for the new session.
     * @param language The language to use for the new session.
     * @return a newly created session.
     */
    public LocalClientSession createClientSession(Connection conn, StreamID id, Locale language) {
        if (serverName == null) {
            throw new IllegalStateException("Server not initialized");
        }
        LocalClientSession session = new LocalClientSession(serverName, conn, id, language);
        conn.init(session);
        // Register to receive close notification on this session so we can
        // remove  and also send an unavailable presence if it wasn't
        // sent before
        //注册Connection的close监听器。
        conn.registerCloseListener(clientSessionListener, session);

        // Add to pre-authenticated sessions.
        localSessionManager.getPreAuthenticatedSessions().put(session.getAddress().getResource(), session);
        // Increment the counter of user sessions
        connectionsCounter.incrementAndGet();
        return session;
    }

二、Connection和Session

  1. connection接口是和:

    boolean validate();

    void init( LocalSession session );

    void reinit( LocalSession session );

    String getHostAddress() throws UnknownHostException;

   
    Certificate[] getLocalCertificates();

    Certificate[] getPeerCertificates();

    void setUsingSelfSignedCertificate( boolean isSelfSigned );

    boolean isUsingSelfSignedCertificate();
   
    @Override
    void close();

  
    void systemShutdown();


    boolean isClosed();


    boolean isSecure();

  
    void registerCloseListener( ConnectionCloseListener listener, Object handbackMessage );

    void removeCloseListener( ConnectionCloseListener listener );

    void deliver( Packet packet ) throws UnauthorizedException;

    void deliverRawText( String text );

    boolean isFlashClient();

    void setFlashClient( boolean flashClient );

    int getMajorXMPPVersion();

    int getMinorXMPPVersion();

    void setXMPPVersion( int majorVersion, int minorVersion );

   
    boolean isCompressed();

    CompressionPolicy getCompressionPolicy();

  
    void setCompressionPolicy(CompressionPolicy compressionPolicy);

 
    TLSPolicy getTlsPolicy();

    void setTlsPolicy(TLSPolicy tlsPolicy);

    PacketDeliverer getPacketDeliverer();

 
    @Deprecated
    void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception;


    void startTLS(boolean clientMode) throws Exception;

    void addCompression();


    void startCompression();


    ConnectionConfiguration getConfiguration();
  1. Session的接口:
@Override
    JID getAddress();

    
    int getStatus();

    StreamID getStreamID();

    String getServerName();
    
    Date getCreationDate();

    Date getLastActiveDate();

    long getNumClientPackets();

    long getNumServerPackets();
    
    void close();

    boolean isClosed();
    
    boolean isSecure();

    Certificate[] getPeerCertificates();

    String getHostAddress() throws UnknownHostException;

    String getHostName() throws UnknownHostException;

    @Override
    void process( Packet packet );

    void deliverRawText( String text );

    boolean validate();
    
    String getCipherSuiteName();

    Locale getLanguage();
  1. Connection和Session的区别:

Connection是接口是对MINA的IoSession和LocalSession的一个包装。IoSession提供了诸如write,read等接口。也就是Connection是io位置的对象,它的一个实现类是NIOConnection,LocalSession是Session实现。
Session是Connection的升华,它的数据是建立在Connection中,它的不同之处是区别不同的Connection,也就是它有诸如JID getAddress()这样的方法,它保存了客户的身份信息。它的一个实现是LocalSession。

三、ConnectionCloseListener和SessionEventListener

  1. ConnectionCloseListener对应Connction
void onConnectionClose( Object handback );
  1. SessionEventListener对应的Session
    void sessionCreated( Session session );

    void sessionDestroyed( Session session );
 
    void anonymousSessionCreated( Session session );
   
    void anonymousSessionDestroyed( Session session );

    void resourceBound( Session session );

总结:我们经常使用的就只有sessionManager这个类和扩展自己的用户验证规则AuthProvider。从sessionManager这个里面获取所有想要的LocalClientSession信息,其次有时候会自己扩展SessionEventListener。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值