DISCUZ 自动登录功能解析
这里以DISCUZ X2.5 为例, X3和X2.5几乎一样
联系作者:addon.discuz.com/?@12744.developer 做企业网站:www.dianzana.com选中自动登录,查看网页代码如下:
<form method="post" autocomplete="off" id="lsform" action="member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes" onsubmit="return lsSubmit();">
<div class="fastlg cl">
<span id="return_ls" style="display:none"></span>
<div class="y pns">
<table cellspacing="0" cellpadding="0">
<tbody><tr>
<td>
<span class="ftid">
<select name="fastloginfield" id="ls_fastloginfield" width="40" tabindex="900" selecti="0" style="display: none;">
<option value="username"></option></select><a href="javascript:;" id="ls_fastloginfield_ctrl" style="width:40px" tabindex="900">用户名</a>
</span>
<script type="text/javascript">simulateSelect("ls_fastloginfield")</script>
</td>
<td><input type="text" name="username" id="ls_username" autocomplete="off" class="px vm" tabindex="901"></td>
<td class="fastlg_l"><label for="ls_cookietime"><input type="checkbox" name="cookietime" id="ls_cookietime" class="pc" value="2592000" tabindex="903">自动登录</label></td>
<td> <a href="javascript:;" onclick="showWindow("login", "member.php?mod=logging&action=login&viewlostpw=1")">找回密码</a></td>
</tr>
<tr>
<td><label for="ls_password" class="z psw_w">密码</label></td>
<td><input type="password" name="password" id="ls_password" class="px vm" autocomplete="off" tabindex="902"></td>
<td class="fastlg_l"><button type="submit" class="pn vm" tabindex="904" style="width: 75px;"><em>登录</em></button></td>
<td> <a href="member.php?mod=register" class="xi2 xw1">立即注册</a></td>
</tr>
</tbody></table>
<input type="hidden" name="quickforward" value="yes">
<input type="hidden" name="handlekey" value="ls">
</div>
</div>
</form>很明显,访问的地址是:member.php?mod=logging&action=login&loginsubmit=yes&infloat=yes&lssubmit=yes, 自动登录复选框的name="cookietime"
依访问路径找到文件:
x25/member.php
x25/source/module/member/member_logging.php 其中 new logging_ctl() 找到
x25/source/class/class_member.php 下面是on_login()方法
function on_login() {
global $_G;
if($_G["uid"]) {
$referer = dreferer();
$ucsynlogin = $this->setting["allowsynlogin"] ? uc_user_synlogin($_G["uid"]) : "";
$param = array("username" => $_G["member"]["username"], "usergroup" => $_G["group"]["grouptitle"], "uid" => $_G["member"]["uid"]);
showmessage("login_succeed", $referer ? $referer : "./", $param, array("showdialog" => 1, "locationtime" => true, "extrajs" => $ucsynlogin));
}
$from_connect = $this->setting["connect"]["allow"] && !empty($_GET["from"]) ? 1 : 0;
$seccodecheck = $from_connect ? false : $this->setting["seccodestatus"] & 2;
$seccodestatus = !empty($_GET["lssubmit"]) ? false : $seccodecheck;
$invite = getinvite();
if(!submitcheck("loginsubmit", 1, $seccodestatus)) {
$auth = "";
$username = !empty($_G["cookie"]["loginuser"]) ? dhtmlspecialchars($_G["cookie"]["loginuser"]) : "";
if(!empty($_GET["auth"])) {
list($username, $password, $questionexist) = explode(" ", authcode($_GET["auth"], "DECODE"));
$username = dhtmlspecialchars($username);
$auth = dhtmlspecialchars($_GET["auth"]);
}
$cookietimecheck = !empty($_G["cookie"]["cookietime"]) || !empty($_GET["cookietime"]) ? "checked="checked"" : "";
if($seccodecheck) {
$seccode = random(6, 1) + $seccode{0} * 1000000;
}
if($this->extrafile && file_exists($this->extrafile)) {
require_once $this->extrafile;
}
$navtitle = lang("core", "title_login");
include template($this->template);
} else {
if(!empty($_GET["auth"])) {
list($_GET["username"], $_GET["password"]) = daddslashes(explode(" ", authcode($_GET["auth"], "DECODE")));
}
if(!($_G["member_loginperm"] = logincheck($_GET["username"]))) {
showmessage("login_strike");
}
if($_GET["fastloginfield"]) {
$_GET["loginfield"] = $_GET["fastloginfield"];
}
$_G["uid"] = $_G["member"]["uid"] = 0;
$_G["username"] = $_G["member"]["username"] = $_G["member"]["password"] = "";
if(!$_GET["password"] || $_GET["password"] != addslashes($_GET["password"])) {
showmessage("profile_passwd_illegal");
}
$result = userlogin($_GET["username"], $_GET["password"], $_GET["questionid"], $_GET["answer"], $this->setting["autoidselect"] ? "auto" : $_GET["loginfield"], $_G["clientip"]);
$uid = $result["ucresult"]["uid"];
if(!empty($_GET["lssubmit"]) && ($result["ucresult"]["uid"] == -3 || $seccodecheck)) {
$_GET["username"] = $result["ucresult"]["username"];
$this->logging_more($result["ucresult"]["uid"] == -3);
}
if($result["status"] == -1) {
if(!$this->setting["fastactivation"]) {
$auth = authcode($result["ucresult"]["username"]." ".FORMHASH, "ENCODE");
showmessage("location_activation", "member.php?mod=".$this->setting["regname"]."&action=activation&auth=".rawurlencode($auth)."&referer=".rawurlencode(dreferer()), array(), array("location" => true));
} else {
$init_arr = explode(",", $this->setting["initcredits"]);
$groupid = $this->setting["regverify"] ? 8 : $this->setting["newusergroupid"];
C::t("common_member")->insert($uid, $result["ucresult"]["username"], md5(random(10)), $result["ucresult"]["email"], $_G["clientip"], $groupid, $init_arr);
$result["member"] = getuserbyuid($uid);
$result["status"] = 1;
}
}
if($result["status"] > 0) {
if($this->extrafile && file_exists($this->extrafile)) {
require_once $this->extrafile;
}
setloginstatus($result["member"], $_GET["cookietime"] ? 2592000 : 0);
checkfollowfeed();
C::t("common_member_status")->update($_G["uid"], array("lastip" => $_G["clientip"], "lastvisit" =>TIMESTAMP, "lastactivity" => TIMESTAMP));
$ucsynlogin = $this->setting["allowsynlogin"] ? uc_user_synlogin($_G["uid"]) : "";
if($invite["id"]) {
$result = C::t("common_invite")->count_by_uid_fuid($invite["uid"], $uid);
if(!$result) {
C::t("common_invite")->update($invite["id"], array("fuid"=>$uid, "fusername"=>$_G["username"]));
updatestat("invite");
} else {
$invite = array();
}
}
if($invite["uid"]) {
require_once libfile("function/friend");
friend_make($invite["uid"], $invite["username"], false);
dsetcookie("invite_auth", "");
if($invite["appid"]) {
updatestat("appinvite");
}
}
$param = array(
"username" => $result["ucresult"]["username"],
"usergroup" => $_G["group"]["grouptitle"],
"uid" => $_G["member"]["uid"],
"groupid" => $_G["groupid"],
"syn" => $ucsynlogin ? 1 : 0
);
$extra = array(
"showdialog" => true,
"locationtime" => true,
"extrajs" => $ucsynlogin
);
$loginmessage = $_G["groupid"] == 8 ? "login_succeed_inactive_member" : "login_succeed";
$location = $invite || $_G["groupid"] == 8 ? "home.php?mod=space&do=home" : dreferer();
if(empty($_GET["handlekey"]) || !empty($_GET["lssubmit"])) {
if(defined("IN_MOBILE")) {
showmessage("location_login_succeed_mobile", $location, array("username" => $result["ucresult"]["username"]), array("location" => true));
} else {
if(!empty($_GET["lssubmit"])) {
if(!$ucsynlogin) {
$extra["location"] = true;
}
showmessage($loginmessage, $location, $param, $extra);
} else {
$href = str_replace(""", """, $location);
showmessage("location_login_succeed", $location, array(),
array(
"showid" => "succeedmessage",
"extrajs" => "<script type="text/javascript">".
"setTimeout("window.location.href ="".$href."";", 3000);".
"$("succeedmessage_href").href = "".$href."";".
"$("main_message").style.display = "none";".
"$("main_succeed").style.display = "";".
"$("succeedlocation").innerHTML = "".lang("message", $loginmessage, $param)."";</script>".$ucsynlogin,
"striptags" => false,
"showdialog" => true
)
);
}
}
} else {
showmessage($loginmessage, $location, $param, $extra);
}
} else {
$password = preg_replace("/^(.{".round(strlen($_GET["password"]) / 4)."})(.+?)(.{".round(strlen($_GET["password"]) / 6)."})$/s", "\1***\3", $_GET["password"]);
$errorlog = dhtmlspecialchars(
TIMESTAMP." ".
($result["ucresult"]["username"] ? $result["ucresult"]["username"] : $_GET["username"])." ".
$password." ".
"Ques #".intval($_GET["questionid"])." ".
$_G["clientip"]);
writelog("illegallog", $errorlog);
loginfailed($_GET["username"]);
$fmsg = $result["ucresult"]["uid"] == "-3" ? (empty($_GET["questionid"]) || $answer == "" ? "login_question_empty" : "login_question_invalid") : "login_invalid";
if($_G["member_loginperm"] > 1) {
showmessage($fmsg, "", array("loginperm" => $_G["member_loginperm"] - 1));
} elseif($_G["member_loginperm"] == -1) {
showmessage("login_password_invalid");
} else {
showmessage("login_strike");
}
}
}
}很明显,表单未提交执行的是:
if(!submitcheck("loginsubmit", 1, $seccodestatus)) {
}这里我们是登录操作,所以只需要看else部分,else部分其中有如下代码:
if($result["status"] > 0) {
if($this->extrafile && file_exists($this->extrafile)) {
require_once $this->extrafile;
}
setloginstatus($result["member"], $_GET["cookietime"] ? 2592000 : 0);
checkfollowfeed();
..........
}$result["status"]>0,肯定是登录成功的,现在来看函数 setloginstatus($result["member"], $_GET["cookietime"] ? 2592000 : 0);
下面找到此函数文件 source/function/function_member.php
function setloginstatus($member, $cookietime) {
global $_G;
$_G["uid"] = intval($member["uid"]);
$_G["username"] = $member["username"];
$_G["adminid"] = $member["adminid"];
$_G["groupid"] = $member["groupid"];
$_G["formhash"] = formhash();
$_G["session"]["invisible"] = getuserprofile("invisible");
$_G["member"] = $member;
loadcache("usergroup_".$_G["groupid"]);
C::app()->session->isnew = true;
C::app()->session->updatesession();
dsetcookie("auth", authcode("{$member["password"]} {$member["uid"]}", "ENCODE"), $cookietime, 1, true);
dsetcookie("loginuser");
dsetcookie("activationauth");
dsetcookie("pmnum");
include_once libfile("function/stat");
updatestat("login", 1);
if(defined("IN_MOBILE")) {
updatestat("mobilelogin", 1);
}
if($_G["setting"]["connect"]["allow"] && $_G["member"]["conisbind"]) {
updatestat("connectlogin", 1);
}
$rule = updatecreditbyaction("daylogin", $_G["uid"]);
if(!$rule["updatecredit"]) {
checkusergroup($_G["uid"]);
}
}其中代码 dsetcookie("auth", authcode("{$member["password"]} {$member["uid"]}", "ENCODE"), $cookietime, 1, true); 把auth保存到cookie中,cookie有效时间是2592000秒,即30天
登录成功后打开Chrome浏览器本地cookie
今天是2014年4月23日,可看到cookie中有3个值的到期时间是2014年5月23日,已经圈红,可能与我们的自动登录有关,其中ZRcL_2132_auth应该就是我们刚才保存的auth
if(empty($this->var["cookie"]["saltkey"])) {
$this->var["cookie"]["saltkey"] = random(8);
dsetcookie("saltkey", $this->var["cookie"]["saltkey"], 86400 * 30, 1, 1);
}
$this->var["authkey"] = md5($this->var["config"]["security"]["authkey"].$this->var["cookie"]["saltkey"]);其中 $this->var 就是全局变量 $_G ,在此文件的 _init_env() 方法中有定义:
$this->var = & $_G;
其实上面的操作就是判断当前是否已经设有cookie saltkey,没有的话随机生成一个,然后保存到cookie中 然后 MD5加密当前的$_config_security_authkey 与 saltkey,保存到$_G["authkey"]中 下面我们看$_G["authkey"]是干什么的,查看 x25/source/function/function_core.php 中的authcode()函数
function authcode($string, $operation = "DECODE", $key = "", $expiry = 0) {
$ckey_length = 4;
$key = md5($key != "" ? $key : getglobal("authkey"));
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == "DECODE" ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : "";
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
$string = $operation == "DECODE" ? base64_decode(substr($string, $ckey_length)) : sprintf("%010d", $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = "";
$box = range(0, 255);
$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == "DECODE") {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
return substr($result, 26);
} else {
return "";
}
} else {
return $keyc.str_replace("=", "", base64_encode($result));
}
}注意其中的 $key = md5($key != "" ? $key : getglobal("authkey")); 原来authkey是默认的加密密钥, 看来之前生成的 auth 是和系统配置文件$_config_security_authkey 及随机值 saltkey 相关的,那么如果没有saltkey,是无法解密auth的,也就无法实现自动登录了。
下面查看 discuz_application.php 中的 _init_user() 方法,其中有如下代码:
if($auth = getglobal("auth", "cookie")) {
$auth = daddslashes(explode(" ", authcode($auth, "DECODE")));
}
list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array("", "") : $auth;
if($discuz_uid) {
$user = getuserbyuid($discuz_uid, 1);
}首先获取当前COOKIE中的auth, $auth = getglotal("auth", "cookie") ,如果有,则执行下面操作:
$auth = daddslashes(explode(" ", authcode($auth, "DECODE")));
解密出auth,然后就直接获取 password 和 uid 了
list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array("", "") : $auth;
至此结束!!!!!!!!
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。
