ejabberd分析(二) 用户注册
来源:互联网 发布:自卑与超越译本 知乎 编辑:程序博客网 时间:2024/05/06 01:20
ejabberd中由ejabberd_c2s处理:
ejabberd_c2s模块启动后gen_fsm的状态位于wait_for_stream
客户端发送
<stream:stream to="localhost" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
服务器端wait_for_stream 函数中经过case xml:get_attr_s("xmlns:stream", Attrs) of?NS_STREAM -> ...... case xml:get_attr_s("version", Attrs) of "1.0" -> send_header(StateData, Server, "1.0", DefaultLang),发送如下的响应给客户端
<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="kinglong" id="fecabb98" xml:lang="en" version="1.0">
由于客户端在注册时并未通过鉴权,所以wait_for_stream 中经过如下路径
case StateData#state.authenticated offalse -> ...... send_element(StateData, {xmlelement, "stream:features", [], TLSFeature ++ CompressFeature ++ [{xmlelement, "mechanisms", [{"xmlns", ?NS_SASL}], Mechs}] ++ ejabberd_hooks:run_fold( c2s_stream_features, Server, [], [Server])}), fsm_next_state(wait_for_feature_request, StateData#state{ server = Server, sasl_state = SASLState, lang = Lang});向客户端发送feature消息,并将当前状态设置为wait_for_feature_request
<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism><mechanism>JIVE-SHAREDSECRET</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><auth xmlns="http://jabber.org/features/iq-auth"/><register xmlns="http://jabber.org/features/iq-register"/></stream:features>
客户端发送给服务器端
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
服务器端函数wait_for_feature_request中经过如下路径,并将状态再次设置为wait_for_stream:
{?NS_TLS, "starttls"} when TLS == true, TLSEnabled == false, SockMod == gen_tcp ->
客户端发送给服务器端
<stream:stream to="kinglong" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
服务器端wait_for_stream中仍然经由以下路径:case xml:get_attr_s("xmlns:stream", Attrs) of?NS_STREAM -> ...... case xml:get_attr_s("version", Attrs) of "1.0" -> ...... case StateData#state.authenticated of false -> ...... send_element(StateData, {xmlelement, "stream:features", [], TLSFeature ++ CompressFeature ++ [{xmlelement, "mechanisms", [{"xmlns", ?NS_SASL}], Mechs}] ++ ejabberd_hooks:run_fold( c2s_stream_features, Server, [], [Server])}),send_element函数发送如下消息给客户端,展示服务器端可提供的鉴权方法,并再次将状态设置为wait_for_feature_request
<?xml version='1.0' encoding='UTF-8'?> <stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="kinglong" id="fecabb98" xml:lang="en" version="1.0"> <stream:features> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanism>DIGEST-MD5</mechanism> <mechanism>JIVE-SHAREDSECRET</mechanism> <mechanism>PLAIN</mechanism> <mechanism>ANONYMOUS</mechanism> <mechanism>CRAM-MD5</mechanism> </mechanisms> <compression xmlns="http://jabber.org/features/compress"> <method>zlib</method> </compression> <auth xmlns="http://jabber.org/features/iq-auth"/> <register xmlns="http://jabber.org/features/iq-register"/> </stream:features>
由于是注册过程,所以客户端并不会从中挑一种开始鉴权过程,而是发送如下的iq消息给服务器端。以下消息查询服务器端注册时所需要的参数
<iq id="2hSnG-4" to="kinglong" type="get"><query xmlns="jabber:iq:register"></query></iq>
服务器端的wait_for_feature_request 函数中按照xmlns属性处理:
case {xml:get_attr_s("xmlns", Attrs), Name} of {?NS_SASL, "auth"} when not ((SockMod == gen_tcp) and TLSRequired) -> ...... {?NS_TLS, "starttls"} when TLS == true, TLSEnabled == false, SockMod == gen_tcp -> ...... {?NS_COMPRESS, "compress"} when Zlib == true, ((SockMod == gen_tcp) or (SockMod == tls)) -> ...... _ -> ....... process_unauthenticated_stanza(StateData, El),由于客户端发送的是iq消息,xmlns会匹配到最后一项。
在process_unauthenticated_stanza函数中:
case jlib:iq_query_info(NewEl) of#iq{} = IQ -> Res = ejabberd_hooks:run_fold(c2s_unauthenticated_iq, StateData#state.server, empty, [StateData#state.server, IQ, StateData#state.ip]),调用含有名为c2s_unauathenticated_iq回调函数的模块来处理iq消息。
此回调函数在mod_register模块中定义:
start(Host, Opts) -> ...... ejabberd_hooks:add(c2s_unauthenticated_iq, Host, ?MODULE, unauthenticated_iq_register, 50), ......其对应于mod_register模块中的unathenticated_iq_register方法
unauthenticated_iq_register(_Acc, Server, #iq{xmlns = ?NS_REGISTER} = IQ, IP) -> Address = case IP of {A, _Port} -> A; _ -> undefined end, ResIQ = process_iq(jlib:make_jid("", "", ""), jlib:make_jid("", Server, ""), IQ, Address), Res1 = jlib:replace_from_to(jlib:make_jid("", Server, ""), jlib:make_jid("", "", ""), jlib:iq_to_xml(ResIQ)), jlib:remove_attr("to", Res1);
以上标蓝的部分即为具体的处理函数。process_iq 将iq按照type 分为两类来处理:process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl, id = ID} = IQ, Source) -> ...... case Type of set -> ...... get ->
本次客户端发送的iq type 为get ,所以匹配到get
至于下面的判断我们目前可以直接无视,匹配到true就OK。
if IsCaptchaEnabled and not IsRegistered -> ..... true -> IQ#iq{type = result, sub_el = [{xmlelement, "query", [{"xmlns", "jabber:iq:register"}], [{xmlelement, "instructions", [], [{xmlcdata, translate:translate( Lang, "Choose a username and password " "to register with this server")}]}, {xmlelement, "username", [], UsernameSubels}, {xmlelement, "password", [], []} | QuerySubels]}]}于是服务器端发送类似如下的响应给客户端:
<iq type="result" id="2hSnG-4" from="kinglong"><query xmlns="jabber:iq:register"><username/><password/><email/><name/><x xmlns="jabber:x:data" type="form"><title>XMPP Client Registration</title><instructions>Choose a username and password to register with this server</instructions><field var="FORM_TYPE" type="hidden"><value>jabber:iq:register</value></field><field var="username" type="text-single" label="Username"><required/></field><field var="name" type="text-single" label="Full name"/><field var="email" type="text-single" label="Email"/><field var="password" type="text-private" label="Password"><required/></field></x></query></iq>
注意,我们在函数process_unauthenticated_stanza处理完当前的iq后状态仍然设置为了wait_for_feature_request。客户端按照服务器要求的参数发送注册信息给服务器:
<iq id="2hSnG-5" to="kinglong" type="set"><query xmlns="jabber:iq:register"><username>15555215557</username><email></email><name></name><password>123</password></query></iq>
由于我们的状态没变,消息同样也是iq消息,type=set 所以本次轮到了set的处理:process_iq(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl, id = ID} = IQ, Source) -> ...... case Type of set -> ...... get ->set 项是一个if 结构的语句:
if (UTag /= false) and (RTag /= false) and AllowRemove -> ...... (UTag == false) and (RTag /= false) and AllowRemove -> ...... (UTag /= false) and (PTag /= false) -> ....... IsCaptchaEnabled -> ....... true -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}UTag、PTag、RTag 分别对应于username,password,remove
正常的注册流程走
(UTag == false) and (RTag /= false) and AllowRemove -> ......try_register_or_set_password( User, Server, Password, From, IQ, SubEl, Source, Lang, not IsCaptchaEnabled)这里有一个比较关键的变量IsCaptchaEnabled 他是模块的配置参数之一,默认为false。
所以我们在调用try_register_or_set_password 时会匹配到如下代码:
_ when CaptchaSucceed ->
具体注册由try_register 函数完成。
经过ip验证后 调用ejabberd_auth:try_register(
User, Server, Password)
ejabberd_auth中遍历配置文件中的每个MOD,并调用try_register/3 方法。注意:这里配置文件中写的只是模块名称的一部分,完整的为:ejabberd_auth_XXXX
例如ejabberd.cfg 中配置为{auth_method, internal}.那么实际调用的为ejabberd_auth_internal:try_register/3
最终我们在try_register/3 中看到如下的代码:
F = fun() ->case mnesia:read({passwd, US}) of [] ->Password2 = case is_scrammed() and is_list(Password) oftrue -> password_to_scram(Password);false -> Password end,mnesia:write(#passwd{us = US, password = Password2}),mnesia:dirty_update_counter( reg_users_counter, LServer, 1),ok; [_E] ->existsendend, mnesia:transaction(F)
这就是最终注册的代码了。
- ejabberd分析(二) 用户注册
- ejabberd分析(二) 用户注册
- ejabberd分析(四) 用户登录
- ejabberd分析(四) 用户登录
- ejabberd源码分析《二》客户端网络监听
- ejabberd16.09源码分析(二) ejabberd启动详解
- 用户注册测试分析
- ejabberd分析(一)
- ejabberd代码分析
- ejabberd线上日志分析
- ejabberd分析(一)
- webwork(二)--用户注册实例
- 【微信小程序 二】用户注册
- 用户登陆注册案例分析
- 用户登录注册案例分析
- 用户登录注册案例分析
- smack连接ejabberd笔记二
- ejabberd分析(三)启动流程
- solr中fieldValueCache使用场景
- 程序员一生
- Netfilter介绍
- Fedora15 添加低版本gcc,多版本gcc共存
- Hadoop简介——文件删除和Task分配
- ejabberd分析(二) 用户注册
- Assembly.Load()不能加载程序集的问题
- Android源码开发中单个模块的编译自动化
- 关注性能: 引用对象(对象引用是怎样严重影响垃圾收集器的)
- linux X86-64 获取系统调用表
- 实用Linux命令,不求最全但求实用-------iptables命令实战
- 代码插入测试
- 方法重载的 依据??
- ORA-01624错误的处理-alter system checkpoint