您的位置:首页 > 运维架构

openfire源码解读-用户登录

2017-05-27 17:43 357 查看
根据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'/> 表示登录成功。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: