openfire源码解读--用户登录
根据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源码解读--用户登录的更多相关文章
- openfire源码解读之将cache和session对象移入redis以提升性能
原文:http://blog.csdn.net/jinzhencs/article/details/50522322 前言: 目前我们的openfire服务器只能支撑单机2W 集群4W.(估测在线用户 ...
- AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...
- 线程本地变量ThreadLocal源码解读
一.ThreadLocal基础知识 原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板 ...
- 从koa-session源码解读session本质
前言 Session,又称为"会话控制",存储特定用户会话所需的属性及配置信息.存于服务器,在整个用户会话中一直存在. 然而: session 到底是什么? session 是存在 ...
- Openfire源码阅读(一)
本篇先分析openfire源码的主要流程,模块细节后续再继续分析: 一.简介: Openfire是开源的实时协作服务器(RTC),它是基于公开协议XMPP(RFC-3920),并在此基础上实现了XMP ...
- Flask(4)- flask请求上下文源码解读、http聊天室单聊/群聊(基于gevent-websocket)
一.flask请求上下文源码解读 通过上篇源码分析,我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__call__方法返回了app的wsgi_app(en ...
- flask的请求上下文源码解读
一.flask请求上下文源码解读 通过上篇源码分析( ---Flask中的CBV和上下文管理--- ),我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__ ...
- v75.01 鸿蒙内核源码分析(远程登录篇) | 内核如何接待远方的客人 | 百篇博客分析OpenHarmony源码
子曰:"不学礼,无以立 ; 不学诗,无以言 " <论语>:季氏篇 百篇博客分析.本篇为: (远程登录篇) | 内核如何接待远方的客人 设备驱动相关篇为: v67.03 ...
- Abp 审计模块源码解读
Abp 审计模块源码解读 Abp 框架为我们自带了审计日志功能,审计日志可以方便地查看每次请求接口所耗的时间,能够帮助我们快速定位到某些性能有问题的接口.除此之外,审计日志信息还包含有每次调用接口时客 ...
随机推荐
- MYSQL 行转列 以及基本的聚合函数count,与group by 以及distinct组合使用
在统计查询中,经常会用到count函数,这里是基础的 MYSQL 行转列 以及基本的聚合函数count,与group by 以及distinct组合使用 -- 创建表 CREATE TABLE `tb ...
- 跟着刚哥梳理java知识点——泛型(十三)
一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: public class GenericTest { public static void main(String[] a ...
- win10 如何配置 java jdk1.8环境变量(2017.2.24)
win10 如何配置 java jdk 环境变量 这里的win10 为全新安装的系统 一.安装 下载 jdk 64位 windows 版本安装(默认安装) 默认安装的路径: C:\Program Fi ...
- C++小技巧之CONTAINING_RECORD
CONTAINING_RECORD Containing record是一个在C++编程中用处很大的一种技巧,它的功能为已知结构体或类的某一成员.对象中该成员的地址以及这一结构体名或类名,从而得到该对 ...
- js的break语句,continue语句,return语句
js的break语句,continue语句,return语句. 用的时候很容易混淆,有过一次泪奔的经历. break语句 break语句会使运行的程序立刻退出包含在最内层的循环或者退出一个switch ...
- TCP三次握手四次挥手过程及各过程中客户端和服务器端的状态。
#三次握手 客户端向服务器端发送SYN包,客户端进入SYN_SEND状态 服务器端收到客户端发送的包返回ACK+SYN包,服务器端进入SYN_RECV状态 客户端收到服务器端返回的包再发回ACK包,客 ...
- 兼容IE8的input输入框的正确使用姿势
input是一个很常见的标签,大家使用的也很常见,但是我在具体的工作中发现要想完美的使用这个标签还是任重而道远,下面是我碰到的几个问题. 1.我们在使用这个标签的时候会习惯的加上placeholder ...
- Redisson入门
Redisson入门 Author:Ricky Date:2017-04-24 Redisson概述 Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Dat ...
- Swift、Objective-C 单例模式 (Singleton)
Swift.Objective-C 单例模式 (Singleton) 本文的单例模式分为严格单例模式和不严格单例模式.单例模式要求一个类有一个实例,有公开接口可以访问这个实例.严格单例模式,要求一个类 ...
- ELK菜鸟手记 (三) - X-Pack权限控制之给Kibana加上登录控制以及index_not_found_exception问题解决
0. 背景 我们在使用ELK进行日志记录的时候,通过网址在Kibana中查看我们的应用程序(eg: Java Web)记录的日志, 但是默认是任何客户端都可以访问Kibana的, 这样就会造成很不安全 ...