因为排查一个问题,顺带着熟悉了一下Discuz!与Ucenter注册和登录的机制,特整理分析。
下面以Discuz! X2.5为例分析代码实现。
1.注册
找到source\class\class_member.php文件,有如下代码:$uid = uc_user_register(addslashes($username), $password, $email, $questionid, $answer, $_G['clientip']);
uc_user_register定义在uc_client\client.php文件,代码如下:function uc_user_register($username, $password, $email, $questiOnid= '', $answer = '', $regip = '') {
return call_user_func(UC_API_FUNC, 'user', 'register', array('username'=>$username, 'password'=>$password, 'email'=>$email, 'questionid'=>$questionid, 'answer'=>$answer, 'regip' => $regip));
}
此函数会回调uc_server下的方法,执行文件为uc_server\control\user.php,执行代码如下:function onregister() {
$this->init_input();
$username = $this->input('username');
$password = $this->input('password');
$email = $this->input('email');
$questiOnid= $this->input('questionid');
$answer = $this->input('answer');
$regip = $this->input('regip');
if(($status = $this->_check_username($username)) <0) {
return $status;
}
if(($status = $this->_check_email($email)) <0) {
return $status;
}
$uid = $_ENV[&#39;user&#39;]->add_user($username, $password, $email, 0, $questionid, $answer, $regip);
return $uid;
}
add_user定义在uc_server\model\user.php文件,代码如下:function add_user($username, $password, $email, $uid = 0, $questiOnid= &#39;&#39;, $answer = &#39;&#39;, $regip = &#39;&#39;) {
$regip = empty($regip) ? $this->base->onlineip : $regip;
$salt = substr(uniqid(rand()), -6);
$password = md5(md5($password).$salt);
$sqladd = $uid ? "uid=&#39;".intval($uid)."&#39;," : &#39;&#39;;
$sqladd .= $questionid > 0 ? " secques=&#39;".$this->quescrypt($questionid, $answer)."&#39;," : " secques=&#39;&#39;,";
$this->db->query("INSERT INTO ".UC_DBTABLEPRE."members SET $sqladd username=&#39;$username&#39;, password=&#39;$password&#39;, email=&#39;$email&#39;, regip=&#39;$regip&#39;, regdate=&#39;".$this->base->time."&#39;, salt=&#39;$salt&#39;");
$uid = $this->db->insert_id();
$this->db->query("INSERT INTO ".UC_DBTABLEPRE."memberfields SET uid=&#39;$uid&#39;");
return $uid;
}
这里会将用户信息写入Ucenter的用户表中。
在这里可以看到用户密码不是用明文存储的,加密的格式为:md5(md5(用户密码) . 6位随机串)
2.登录
找到source\class\class_member.php文件,有如下代码:$result = userlogin($_GET[&#39;username&#39;], $_GET[&#39;password&#39;], $_GET[&#39;questionid&#39;], $_GET[&#39;answer&#39;], $this->setting[&#39;autoidselect&#39;] ? &#39;auto&#39; : $_GET[&#39;loginfield&#39;], $_G[&#39;clientip&#39;]);
userlogin定义在source\function\function_member.php文件,函数内部有如下代码:if($isuid == 3) {
if(!strcmp(dintval($username), $username)) {
$return[&#39;ucresult&#39;] = uc_user_login($username, $password, 1, 1, $questionid, $answer, $ip);
} elseif(isemail($username)) {
$return[&#39;ucresult&#39;] = uc_user_login($username, $password, 2, 1, $questionid, $answer, $ip);
}
if($return[&#39;ucresult&#39;][0] <= 0 && $return[&#39;ucresult&#39;][0] != -3) {
$return[&#39;ucresult&#39;] = uc_user_login(addslashes($username), $password, 0, 1, $questionid, $answer, $ip);
}
} else {
$return[&#39;ucresult&#39;] = uc_user_login(addslashes($username), $password, $isuid, 1, $questionid, $answer, $ip);
}
uc_user_login定义在uc_client\client.php文件,代码如下:function uc_user_login($username, $password, $isuid = 0, $checkques = 0, $questiOnid= &#39;&#39;, $answer = &#39;&#39;) {
$isuid = intval($isuid);
$return = call_user_func(UC_API_FUNC, &#39;user&#39;, &#39;login&#39;, array(&#39;username&#39;=>$username, &#39;password&#39;=>$password, &#39;isuid&#39;=>$isuid, &#39;checkques&#39;=>$checkques, &#39;questionid&#39;=>$questionid, &#39;answer&#39;=>$answer));
return UC_COnNECT== &#39;mysql&#39; ? $return : uc_unserialize($return);
}
此函数会回调uc_server下的方法,执行文件为uc_server\control\user.php,执行代码如下:function onlogin() {
$this->init_input();
$isuid = $this->input(&#39;isuid&#39;);
$username = $this->input(&#39;username&#39;);
$password = $this->input(&#39;password&#39;);
$checkques = $this->input(&#39;checkques&#39;);
$questiOnid= $this->input(&#39;questionid&#39;);
$answer = $this->input(&#39;answer&#39;);
if($isuid == 1) {
$user = $_ENV[&#39;user&#39;]->get_user_by_uid($username);
} elseif($isuid == 2) {
$user = $_ENV[&#39;user&#39;]->get_user_by_email($username);
} else {
$user = $_ENV[&#39;user&#39;]->get_user_by_username($username);
}
$passwordmd5 = preg_match(&#39;/^\w{32}$/&#39;, $password) ? $password : md5($password);
if(empty($user)) {
$status = -1;
} elseif($user[&#39;password&#39;] != md5($passwordmd5.$user[&#39;salt&#39;])) {
$status = -2;
} elseif($checkques && $user[&#39;secques&#39;] != &#39;&#39; && $user[&#39;secques&#39;] != $_ENV[&#39;user&#39;]->quescrypt($questionid, $answer)) {
$status = -3;
} else {
$status = $user[&#39;uid&#39;];
}
$merge = $status != -1 && !$isuid && $_ENV[&#39;user&#39;]->check_mergeuser($username) ? 1 : 0;
return array($status, $user[&#39;username&#39;], $password, $user[&#39;email&#39;], $merge);
}
这里会验证用户输入的密码是否和Ucenter里存储的密码一致。
验证的格式为:首先会验证用户密码是否为32位,如果不是则对用户输出的密码进行md5处理。(32位验证是后台设置的加密传输密码,开启后会先进行md5然后才传递给Ucenter)
Ucenter里存储的对应用户的加密后的密码 == md5(格式化后的用户输入的密码 . Ucenter里存储的对应用户的6位随机串)