根据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. netcore实践:跨平台动态加载native组件

    缘起netcore框架下实现基于zmq的应用. 在.net framework时代,我们进行zmq开发由很多的选择,比较常用的有clrzmq4和NetMQ. 其中clrzmq是基于libzmq的Int ...

  2. servlet:从入门到实战学习(1)---全·环境配置

    最近公司忙加班学习,学校忙助教工作,博客鸽了好久,后端学习工作过程中学了好多东西,趁着工作之余得空补补博客,算是整理下学习的东西. javaweb的后端研发需要学习的是tomcat+servlet+j ...

  3. NODEJS环境搭建 第一篇 安装和部署NODEJS

    一.下载安装文件 根据自己当前系统环境,下载相对应的安装文件 https://nodejs.org/en/download/ 二.双击安装 都傻瓜式的安装步骤,一步一步安装就好了. 三.检查安装结果 ...

  4. 跨语言时区处理与Epoch

    国际化通用程序或标准协议通常都涉及到时区问题,比如最近项目用到的OIDC(OpenID Connect). OIDC基于OAuth2协议,其id_token中包含了exp来表达该Token的过期时间, ...

  5. Linux-进程描述(5)之进程环境

    main函数和启动例程 当内核使用一个exec函数执行C程序时,在调用main函数之前先调用一个特殊的启动例程,可执行程序将此例程指定为程序的起始地址.启动例程从内核获取命令行参数和环境变量,然后为调 ...

  6. 使用 Composer 安装 Laravel 框架

    前言: 1. Composer 安装 Laravel 有两种方式: 第一种是通过 Composer 的 create-project 命令安装 Laravel 框架, 第二种是先通过 Composer ...

  7. 移动端H5开发 (滑动事件)

    最近一直在做手机App H5的开发,在开发过程中,经常会遇到很多滑动事件,写个demo,分享自己的一些写法.(如写的不好,轻喷!) 直接贴代码 html css代码 <!DOCTYPE html ...

  8. 最长公共子序列poj1458

    #include<map> #include<set> #include<list> #include<cmath> #include<queue ...

  9. poj3264线段数求最大最小值

    链接:https://vjudge.net/contest/66989#problem/G 完完全全的水题,还是被坑了,一个return忘了写,de了半天bug!! #include<iostr ...

  10. linux 内核的另一个自旋锁 - 读写锁

    除spinlock外,linux 内核还有一个自旋锁,名为arch_rwlock_t.它的头文件是qrwlock.h,包含在spinlock.h,头文件中对它全称为"Queue read/w ...