openfire源碼解讀--用戶登錄
阿新 • • 發佈:2017-05-27
users lns log requested ipa ram ins 需要 either
根據xmpp協議
客戶端發送:
<auth xmlns=‘urn:ietf:params:xml:ns:xmpp-sasl‘ mechanism=‘PLAIN‘>XXXXXXXXXXXXXXXXXXXXX=</auth>
其中,xmlns是命名空間,mechanism是用戶名密碼的加密方式,auth 標簽的text內容為用戶名密碼通過PLAIN方式加密的字符串。
服務端接收:
通過ConnectionHandler類的messageReceived方法接收,process中處理
else if ("auth".equals(tag)) { // User is trying to authenticate using SASL startedSASL = true; // Process authentication stanza saslStatus = SASLAuthentication.handle(session, doc); }
判斷xml標簽為auth時進行登錄驗證。
下面來看SASLAuthentication的處理
首先判斷加密方式,然後解密,通過下面這個方法來驗證登錄。
final byte[] challenge = saslServer.evaluateResponse( decoded ); // Either a challenge or success data.
根據加密方式不同,驗證處理方法不同。PLAIN加密的,那就看SaslServerPlainImpl中是怎麽實現的。
NameCallback ncb = new NameCallback("PLAIN authentication ID: ",principal); VerifyPasswordCallback vpcb = new VerifyPasswordCallback(password.toCharArray()); cbh.handle(new Callback[]{ncb,vpcb}); if (vpcb.getVerified()) { vpcb.clearPassword(); AuthorizeCallback acb = new AuthorizeCallback(principal,username); cbh.handle(new Callback[]{acb}); if(acb.isAuthorized()) { username = acb.getAuthorizedID(); completed = true; } else { completed = true; username = null; throw new SaslException("PLAIN: user not authorized: "+principal); } } else { throw new SaslException("PLAIN: user not authorized: "+principal); }
可以看到openfire是通過callback來驗證的,而且還進行了2層驗證。第一次是驗證用戶名密碼,第二次是加載用戶信息
(自己有需要修改源碼時,這裏就可以優化了,第一步登錄驗證時就可以獲取用戶信息了,沒必要重新查詢一次)。
callback是通過XMPPCallbackHandler實現的。
for (Callback callback : callbacks) { if (callback instanceof RealmCallback) { ((RealmCallback) callback).setText( XMPPServer.getInstance().getServerInfo().getXMPPDomain() ); } else if (callback instanceof NameCallback) { name = ((NameCallback) callback).getName(); if (name == null) { name = ((NameCallback) callback).getDefaultName(); } //Log.debug("XMPPCallbackHandler: NameCallback: " + name); } else if (callback instanceof PasswordCallback) { try { // Get the password from the UserProvider. Some UserProviders may not support // this operation ((PasswordCallback) callback) .setPassword(AuthFactory.getPassword(name).toCharArray()); //Log.debug("XMPPCallbackHandler: PasswordCallback"); } catch (UserNotFoundException | UnsupportedOperationException e) { throw new IOException(e.toString()); } } else if (callback instanceof VerifyPasswordCallback) { //Log.debug("XMPPCallbackHandler: VerifyPasswordCallback"); VerifyPasswordCallback vpcb = (VerifyPasswordCallback) callback; try { AuthToken at = AuthFactory.authenticate(name, new String(vpcb.getPassword())); vpcb.setVerified((at != null)); } catch (Exception e) { vpcb.setVerified(false); } } else if (callback instanceof AuthorizeCallback) { //Log.debug("XMPPCallbackHandler: AuthorizeCallback"); AuthorizeCallback authCallback = ((AuthorizeCallback) callback); // Principal that authenticated String principal = authCallback.getAuthenticationID(); // Username requested (not full JID) String username = authCallback.getAuthorizationID(); // Remove any REALM from the username. This is optional in the spec and it may cause // a lot of users to fail to log in if their clients is sending an incorrect value if (username != null && username.contains("@")) { username = username.substring(0, username.lastIndexOf("@")); } if (principal.equals(username)) { //client perhaps made no request, get default username username = AuthorizationManager.map(principal); if (Log.isDebugEnabled()) { //Log.debug("XMPPCallbackHandler: no username requested, using " + username); } } if (AuthorizationManager.authorize(username, principal)) { if (Log.isDebugEnabled()) { //Log.debug("XMPPCallbackHandler: " + principal + " authorized to " + username); } authCallback.setAuthorized(true); authCallback.setAuthorizedID(username); } else { if (Log.isDebugEnabled()) { //Log.debug("XMPPCallbackHandler: " + principal + " not authorized to " + username); } authCallback.setAuthorized(false); } }
第一次驗證用戶名密碼是通過 AuthToken at = AuthFactory.authenticate(name, new String(vpcb.getPassword()));驗證的
根據數據庫配置provider.auth.className的類實現登錄驗證。
第二次驗證AuthorizationManager.authorize(username, principal)會加載用戶信息。
2次驗證通過就會返回客戶端 <success xmlns=‘urn:ietf:params:xml:ns:xmpp-sasl‘/> 表示登錄成功。
openfire源碼解讀--用戶登錄