根据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源码解读--用户登录的更多相关文章

  1. openfire源码解读之将cache和session对象移入redis以提升性能

    原文:http://blog.csdn.net/jinzhencs/article/details/50522322 前言: 目前我们的openfire服务器只能支撑单机2W 集群4W.(估测在线用户 ...

  2. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  3. 线程本地变量ThreadLocal源码解读

      一.ThreadLocal基础知识 原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板 ...

  4. 从koa-session源码解读session本质

    前言 Session,又称为"会话控制",存储特定用户会话所需的属性及配置信息.存于服务器,在整个用户会话中一直存在. 然而: session 到底是什么? session 是存在 ...

  5. Openfire源码阅读(一)

    本篇先分析openfire源码的主要流程,模块细节后续再继续分析: 一.简介: Openfire是开源的实时协作服务器(RTC),它是基于公开协议XMPP(RFC-3920),并在此基础上实现了XMP ...

  6. Flask(4)- flask请求上下文源码解读、http聊天室单聊/群聊(基于gevent-websocket)

    一.flask请求上下文源码解读 通过上篇源码分析,我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__call__方法返回了app的wsgi_app(en ...

  7. flask的请求上下文源码解读

    一.flask请求上下文源码解读 通过上篇源码分析( ---Flask中的CBV和上下文管理--- ),我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__ ...

  8. v75.01 鸿蒙内核源码分析(远程登录篇) | 内核如何接待远方的客人 | 百篇博客分析OpenHarmony源码

    子曰:"不学礼,无以立 ; 不学诗,无以言 " <论语>:季氏篇 百篇博客分析.本篇为: (远程登录篇) | 内核如何接待远方的客人 设备驱动相关篇为: v67.03 ...

  9. Abp 审计模块源码解读

    Abp 审计模块源码解读 Abp 框架为我们自带了审计日志功能,审计日志可以方便地查看每次请求接口所耗的时间,能够帮助我们快速定位到某些性能有问题的接口.除此之外,审计日志信息还包含有每次调用接口时客 ...

随机推荐

  1. Ranklib源码剖析--LambdaMart

    Ranklib是一套优秀的Learning to Rank领域的开源实现,其中有实现了MART,RankNet,RankBoost,LambdaMart,Random Forest等模型.其中由微软发 ...

  2. js和android及ios交互

    Android中Java和JavaScript交互 这种交互,Hybrid App 会用的比较多一点, 本文将介绍如何实现Java代码和Javascript代码的相互调用. Android提供了一个很 ...

  3. ecshop打开手机端QQ对话窗口

    ecshop的手机功能比较弱,默认的模板很丑,也不带打开手机端QQ等功能,在程序里添加im等程序变量后,在模板里引入以下代码,即可打开手机端qq对话窗口 <!-- QQ 号码 {foreach ...

  4. MongoDB系列:把mongodb作为windows的服务来启动

    1.首先切换到mongodb安装目录下的bin目录,在控制台直接运行以下命令 "C:\Program Files\MongoDB\Server\3.0\bin\mongod.exe" ...

  5. Extjs6(三)——用extjs6.0写一个简单页面

    本文基于ext-6.0.0 一.关于border布局 在用ext做项目的过程中,最常用到的一种布局就是border布局,现在要写的这个简单页面也是运用border布局来做.border布局将页面分为五 ...

  6. java 集合框架(TreeSet操作,自动对数据进行排序,重写CompareTo方法)

    /*TreeSet * treeSet存入数据后自动调用元素的compareTo(Object obj) 方法,自动对数据进行排序 * 所以输出的数据是经过排序的数据 * 注:compareTo方法返 ...

  7. Oracle空间查询 ORA-28595

    可使用数据库管理系统 (DBMS) 的结构化查询语言 (SQL).数据类型和表格式来处理地理数据库或安装了 ST_Geometry 类型的数据库中所存储的信息. 例如,在ArcMap中我们使用&quo ...

  8. react基于nodejs简单的搭建与开发方法

    只需安装babel命令,即可将react的jsx写法转换成浏览器认识的js写法 1.安装nodejs(百度下载安装即可,自带npm) 2.cmd打开命令行,cd进入在自己的文件夹下 执行命令: npm ...

  9. Asp.Net 网站一键部署技术(上)

    用垃圾而不稳定的网速上传N次压缩包都传不上去? 手动决定哪些覆盖不覆盖? 覆盖了web.config又要手动修改连接字符串? 不注意把原有配置搞丢了? …… 貌似从此早下班和休假与你无缘了. 所以!! ...

  10. 表单提交音乐文件(php)

    利用点空闲时间来写个博客,最近做的项目中需要表单提交音频的,图片的,各种类型,把它存到数据库里,这里先来说一下音乐文件的表单提交吧,后几天再来更新输入数据库的,先看一下效果 点击浏览 就会出来预览,点 ...