标签 Discuz 下的文章

Ucenter的用户注册和登录分析

因为排查一个问题,顺带着熟悉了一下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['user']->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 = '', $answer = '', $regip = '') {
	$regip = empty($regip) ? $this->base->onlineip : $regip;
	$salt = substr(uniqid(rand()), -6);
	$password = md5(md5($password).$salt);
	$sqladd = $uid ? "uid='".intval($uid)."'," : '';
	$sqladd .= $questionid > 0 ? " secques='".$this->quescrypt($questionid, $answer)."'," : " secques='',";
	$this->db->query("INSERT INTO ".UC_DBTABLEPRE."members SET $sqladd username='$username', password='$password', email='$email', regip='$regip', regdate='".$this->base->time."', salt='$salt'");
	$uid = $this->db->insert_id();
	$this->db->query("INSERT INTO ".UC_DBTABLEPRE."memberfields SET uid='$uid'");
	return $uid;
}

这里会将用户信息写入Ucenter的用户表中。
在这里可以看到用户密码不是用明文存储的,加密的格式为:

md5(md5(用户密码) . 6位随机串)

2.登录

找到source\class\class_member.php文件,有如下代码:

$result = userlogin($_GET['username'], $_GET['password'], $_GET['questionid'], $_GET['answer'], $this->setting['autoidselect'] ? 'auto' : $_GET['loginfield'], $_G['clientip']);

userlogin定义在source\function\function_member.php文件,函数内部有如下代码:

if($isuid == 3) {
	if(!strcmp(dintval($username), $username)) {
		$return['ucresult'] = uc_user_login($username, $password, 1, 1, $questionid, $answer, $ip);
	} elseif(isemail($username)) {
		$return['ucresult'] = uc_user_login($username, $password, 2, 1, $questionid, $answer, $ip);
	}
	if($return['ucresult'][0] <= 0 && $return['ucresult'][0] != -3) {
		$return['ucresult'] = uc_user_login(addslashes($username), $password, 0, 1, $questionid, $answer, $ip);
	}
} else {
	$return['ucresult'] = 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 = '', $answer = '') {
	$isuid = intval($isuid);
	$return = call_user_func(UC_API_FUNC, 'user', 'login', array('username'=>$username, 'password'=>$password, 'isuid'=>$isuid, 'checkques'=>$checkques, 'questionid'=>$questionid, 'answer'=>$answer));
	return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
}

此函数会回调uc_server下的方法,执行文件为uc_server\control\user.php,执行代码如下:

function onlogin() {
	$this->init_input();
	$isuid = $this->input('isuid');
	$username = $this->input('username');
	$password = $this->input('password');
	$checkques = $this->input('checkques');
	$questionid = $this->input('questionid');
	$answer = $this->input('answer');
	if($isuid == 1) {
		$user = $_ENV['user']->get_user_by_uid($username);
	} elseif($isuid == 2) {
		$user = $_ENV['user']->get_user_by_email($username);
	} else {
		$user = $_ENV['user']->get_user_by_username($username);
	}

	$passwordmd5 = preg_match('/^\w{32}$/', $password) ? $password : md5($password);
	if(empty($user)) {
		$status = -1;
	} elseif($user['password'] != md5($passwordmd5.$user['salt'])) {
		$status = -2;
	} elseif($checkques && $user['secques'] != '' && $user['secques'] != $_ENV['user']->quescrypt($questionid, $answer)) {
		$status = -3;
	} else {
		$status = $user['uid'];
	}
	$merge = $status != -1 && !$isuid && $_ENV['user']->check_mergeuser($username) ? 1 : 0;
	return array($status, $user['username'], $password, $user['email'], $merge);
}

这里会验证用户输入的密码是否和Ucenter里存储的密码一致。
验证的格式为:

首先会验证用户密码是否为32位,如果不是则对用户输出的密码进行md5处理。(32位验证是后台设置的加密传输密码,开启后会先进行md5然后才传递给Ucenter)
Ucenter里存储的对应用户的加密后的密码 == md5(格式化后的用户输入的密码 . Ucenter里存储的对应用户的6位随机串)

游客可见游客发布的待审核帖子的bug分析

话说好久没捉虫了,今天突然发现个,哈哈~
此bug在Discuz! x2版本上发现。

具体表现为:
版块开启游客发表权限,发表新主题进入待审核。
在游客下发布帖子,提示进入审核,但游客状态下可以直接浏览。

代码分析:
source\function\function_forum.php文件,有如下代码

if(!$_G['forum_auditstatuson'] && !empty($_G['thread'])
		&& !($_G['thread']['displayorder'] >= 0 || (in_array($_G['thread']['displayorder'], array(-4,-3,-2)) && $_G['thread']['authorid'] == $_G['uid']))) {
	$_G['thread'] = null;
}

这里后面的$_G['thread']['authorid'] == $_G['uid']没有对$_G['uid']是否存在进行判断。

修复方案:
在$_G['thread']['authorid'] == $_G['uid']后面加上下面的代码:

 && $_G['uid']

此bug在Discuz! x25下未发现。

修改Discuz的微博来源

Discuz新版本中增加了分享到微博的功能,分享到微博的时候可以设置自己的来源字段。

目前带有分享功能的Discuz版本一共有两类:

一种是Discuz! X2版本的云平台版,里面集成了QQ互联功能,带有分享到微博、Qzone和朋友社区的功能。
另外一种是QQ互联插件版的Discuz,主要是Discuz! X1.5和Discuz! 7.2两个版本。

修改前需要有AppKey,什么是AppKey呢?

这个可以在open.t.qq.com下的我的应用里查看你自己的应用,前提需要你自己申请一个应用,如图里的AppKey就是后面我们要用到的KEY。

下面针对不同的版本给出不同的修改方法:

1.针对于Discuz! X2云平台版:

进入站点后台->云平台->QQ互联下,点击开启Q-Share功能下的是,然后在腾讯微博开放平台AppKey里填写你申请的AppKey,如图。

2.针对于1.5插件版本:

找到source\plugin\qqconnect\connect.class.php文件
搜索代码

$_G['connect']['weibo_public_appkey'] = 'ce7fb946290e4109bdc9175108b6db3a';

将ce7fb946290e4109bdc9175108b6db3a改为你在腾讯微博开放平台(http://open.t.qq.com)下申请到的APPKEY。

3.针对于7.2插件版本:

找到plugins\qqconnect\connect.class.php文件
搜索代码

$connect_setting['weibo_public_appkey'] = 'ce7fb946290e4109bdc9175108b6db3a';

将ce7fb946290e4109bdc9175108b6db3a改为你在腾讯微博开放平台(http://open.t.qq.com)下申请到的APPKEY。