根据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. [转载]转载一篇好文章作为Java与面向对象之随感(3)

    关于对象与引用之间的一些基本概念. 初学Java时,在很长一段时间里,总觉得基本概念很模糊.后来才知道,在许多Java书中,把对象和对象的引用混为一谈.可是,如果我分不清对象与对象引用, 那实在没法很 ...

  2. .NET遇上Docker - 使用Docker Compose组织Ngnix和.NETCore运行

    本文工具准备: Docker for Windows Visual Studio 2015 与 Visual Studio Tools for Docker 或 Visual Studio 2017 ...

  3. 浅谈MVC页面之间参数传递

    关于MVC页面之间的传值,有多种方式,下面,我们就Html.RenderAction 方式 和 Html.RenderPartial 方式 来给大家分享一下有什么不同. 一.Html.RenderAc ...

  4. Nodejs核心模块

    (1)全局对象 在浏览器JS中,通常window是全局对象,而nodejs中的全局对象是global,所有全局变量都是global对象的属性. 在nodejs中能够直接访问到的对象通常都是global ...

  5. win8.1启用ahci后蓝屏

    先简单介绍一下,本应该win7开始,系统安装的时候默认就启用了ahci硬盘模式.但是博主犯了傻,装了win8.1后安装win XP形成双系统.xp并不支持ahci模式,所以将硬盘模式改成了IDE模式, ...

  6. C++学习笔记1(扩充:C++中的格式控制)

    前一章,我们了解了再C++中的标准的输入输出问题,那么肯能就有人会问了再C语言中我们可以灵活的控制输出和显示,那么再再C++中可以实现吗?我的回答是当然可以的,只不过再C++中的控制可能相比较而言要比 ...

  7. AngularJS创建新指令 - 函数功能

    首先先介绍下AngularJS指令下的几种函数 Link函数和Scope 指令生成出的模板其实没有太多意义,除非它在特定的scope下编译.默认情况下,指令并不会创建新的子scope.更多的,它使用父 ...

  8. CF #284 div1 D. Traffic Jams in the Land 线段树

    大意是有n段路,每一段路有个值a,通过每一端路需要1s,如果通过这一段路时刻t为a的倍数,则需要等待1s再走,也就是需要2s通过. 比较头疼的就是相邻两个数之间会因为数字不同制约,一开始想a的范围是2 ...

  9. Winform 使用DotNetBar 根据菜单加载TabControl

    winform 如何使用TabControl 控件来做winform界面框架? 这样的效果: 首先菜单的窗口展示的承载器为TabControl 控件,这个控件本身包含多页面预览和页面初始化. 如图所示 ...

  10. PHP 魔术方法__set() __get() 方法

    a); //output: 123 var_dump($s->b); //output: 123 var_dump($s->c); //output: null var_dump($s-& ...