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 框架为我们自带了审计日志功能,审计日志可以方便地查看每次请求接口所耗的时间,能够帮助我们快速定位到某些性能有问题的接口.除此之外,审计日志信息还包含有每次调用接口时客 ...
随机推荐
- 1135: 零起点学算法42——多组测试数据(求和)IV
1135: 零起点学算法42--多组测试数据(求和)IV Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted ...
- web前端概念巩固(一)
h5: 1.web语义化 Web语义化是指在进行HTML结构.表现.行为设计时,尽量使用语义化的标签,使程序代码简介明了,易于进行Web操作和网站SEO,方便团队协作的一种标准,以图实现一种" ...
- 关于Java中继承多接口同名方法的问题
在Java中如果一个类同时继承接口A与B,并且这两个接口中具有同名方法,会怎么样? 动手做实验: interface A{ void fun(); } interface B{ void fun(); ...
- Java 代码安全(一) —— 避免用String储存敏感数据
Java 代码安全(一) -- 避免用String储存敏感数据 如果重要的数据(保存在内存中)在使用后没有及时清理,有可能会导致信息泄漏.开发人员通常都回用String 保存敏感数据(密码, ...
- 分享一个随机更改 MAC地址 软件
有些软件 是根据 MAC地址 来判断 是不是 已经 安装过 这个 软件 (针对 有些软件是 可以 免费 使用的 ) 要想 一直 使用 的话 只需要 修改一下 mac地址 就可以 继续 使用! 在百度中 ...
- Java数据结构和算法
首先,本人自学java,但是只学习了java的基础知识,所以想接下来学习一下数据结构和算法,但是找了很多教材,大部分写的好的都是用c语言实现的,虽然知道数据结构和算法,跟什么语言实现的没有关系,但是我 ...
- [.NET] 《Effective C#》读书笔记(二)- .NET 资源托管
<Effective C#>读书笔记(二)- .NET 资源托管 简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内 ...
- javascript中的apply,call,bind详解
apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Jav ...
- linux下python+pycharm安装
安装环境: vmware 12 centos 6.8 一.安装python3.5 默认情况下,linux下是默认使用2.x版本的,现在我们要安装3.x版本,具体操作如下 1.去官网下载安装包.(这 ...
- react native 升级到0.31.0的相关问题 mac Android Studio开发环境
报错Caused by: java.lang.ClassCastException: android.app.Application cannot be cast to com.facebook.re ...