There are questions remain, We'll search for the answers together. But one thing we known for sure,the future is not set!

【原创文章】追查Ectouch QQ登录每次用户名都不同的原因和解决办法

ectouch 百蔬君 4902℃ 已收录 0评论

前日,同事给我说,有一个客户充了10块钱,但是在自己的账户里面找不到,不知道钱哪里去了,在后台留言询问我们,以为我们是钓鱼网站。

Snap1

凡是涉及到金钱的事情都是大事情,马上检查时什么原因。首先让同事检查了,是否有这笔钱,经过财务查看,在当前确实有一笔10块钱的进入。同进入后台查看了留言客户的信息,却确实没有余额。

Snap2

让人有一点小紧张,马上去排查到底是怎么一个情况。

经过反复几轮的测试,我们发现这个用户是通过手机端登陆的,也就是ectouch的QQ登陆!

经过蛮久的代码检查和搜索,总算是明白ectouch这个qq登陆的一个流程和思路。跟我在《从代码深探ectouch商品销售为0的原因及详解索源追码思路》和《解决ectouch中快递费用为零的问题及介绍增加配送方式的方法》中提到的一样,ectouch与ecshop整合的思路是纠结的。

ectouch控制qq第三方登录的代码控制在\mobile\include\apps\default\controller\UserController.class.php中的public function third_login函数。既然能够登陆成功,那么说明问题出在登陆之后,其控制登陆后的代码如下

// 授权成功 返回登录
            if ($obj->call_back($info, $url, $_GET['code'])) {
                if ($_SESSION['access_token']) {
                    $res = new $type($info, $_SESSION['access_token']);
                    $openid = $res->get_openid();
                    // 获取用户信息
                    $userinfo = $res->get_user_info($openid);
                    // 处理数据
                    $userinfo['aite_id'] = $type . '_' . $openid; // 添加登录标示
                    if ($userinfo['user_name'] = model('Users')->get_one_user($userinfo['aite_id'])) {
                        // 已有记录
                        self::$user->set_session($userinfo['user_name']);
                        self::$user->set_cookie($userinfo['user_name']);
                        model('Users')->update_user_info();
                        model('Users')->recalculate_price();
                        $jump_url = empty($this->back_act) ? url('index') : $this->back_act;
                        $this->redirect($jump_url);
                    }
                    $userinfo['user_name'] = substr($openid, -6);
                    if(self::$user->check_user($userinfo['user_name'])) {
                        $userinfo['user_name'] = $userinfo['user_name'].rand(1000, 9999); // 重名处理
                    }
                    $userinfo['email'] = empty($userinfo['email']) ? $userinfo['user_name'] . '@' . get_top_domain() : $userinfo['email'];
                    // 插入数据库
                    model('Users')->third_reg($userinfo);
                    self::$user->set_session($userinfo['user_name']);
                    self::$user->set_cookie($userinfo['user_name']);
                    model('Users')->update_user_info();
                    model('Users')->recalculate_price();
                    $jump_url = empty($this->back_act) ? url('index') : $this->back_act;
                    $this->redirect($jump_url);
                }
            } else {
                show_message(L('process_false'), L('relogin_lnk'), url('login', array(
                    'referer' => urlencode($this->back_act)
                        )), 'error');
            }

在这里,因为一个非常不应该出现的错误,耽误了我相当长的时间。看到这一段代码,很多人应当都有一个疑问,$userinfo['user_name']的赋值在哪里?
为了测试,我一直想获取$userinfo['user_name']的值,但是每次都是获取空值,无奈之下,自己随便赋值一个测试一下$userinfo['user_name']='test';,用一个网站系统已经已经存在的用户名做测试,但是逻辑过程并没有进入if处理过程,更为奇妙的是我在$userinfo['user_name'] = substr($openid, -6);前面截取$userinfo['user_name']的值竟然还是空值!!在这里我追查了get_one_user($userinfo[‘aite_id’])的结果,也是空值,跑去看了这个函数的代码。
这个函数位于\mobile\include\apps\default\model\UsersModel.class.php,代码如下

 /**
* 检查该用户是否启动过第三方登录
* @param type $aite_id
* @return type
*/
function get_one_user($aite_id) {
$sql = 'SELECT u.user_name FROM ' . $this->pre . 'users u LEFT JOIN ' . $this->pre .
'touch_user_info t ON t.user_id = u.user_id WHERE t.aite_id = "' . $aite_id . '" ';
$res = $this->row($sql);
return $res['user_name'];
}

可以看到这个函数是在users和touch_user_info中联合查询相同user_id对应的user_name,跑去数据库一看,touch_user_info的user_id全是0,这样查询一辈子也不会相等了,也就不会有user_name了。

纠结这个事情的时候,更为纠结的代码来了, if ($userinfo['user_name'] = model('Users')->get_one_user($userinfo['aite_id'])) {,玩过php代码的程序员应当都知道,这个判断成立的条件是只要get_one_user有返回值不为假就成立!在php中=是赋值,并不是判断两边是否相等!也算找到为何在$userinfo['user_name'] = substr($openid, -6);前面截取user_name也是空值了!

既然 $userinfo = $res->get_user_info($openid);,那么这个$userinfo 是从这个函数继承的。很容易追查到这个函数来自于ectouch的qq插件文件\mobile\plugins\connect\qq.php,经过无数次测试,我确认这个插件是perfect的,非常精简有效!同时也摸清了qq第三方登录的一个流程。相关资料在http://wiki.open.qq.com/wiki/website/API%E5%88%97%E8%A1%A8http://wiki.open.qq.com/wiki/website/get_user_info

通过官方文档可以看到,qq返回的json数据如下:

Content-type: text/html; charset=utf-8 
{
"ret":0,
"msg":"",
"nickname":"Peter",
"figureurl":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/30",
"figureurl_1":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/50",
"figureurl_2":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/100",
"figureurl_qq_1":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/40",
"figureurl_qq_2":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/100",
"gender":"男",
"is_yellow_vip":"1",
"vip":"1",
"yellow_vip_level":"7",
"level":"7",
"is_yellow_year_vip":"1"
} 

ret,msg,nickname等等就是变量名。可以看到,这里没有user_name这样一个变量,那么回到third_login函数,看原来的代码,就可以知道为什么每次用同一个qq登陆网站,但是用户名都不同。每一次登陆, if ($userinfo['user_name'] = model('Users')->get_one_user($userinfo['aite_id'])) {if判断都不成立,于是在 $userinfo['user_name'] = substr($openid, -6);进行了随机命名,在

if(self::$user->check_user($userinfo['user_name'])) {
 $userinfo['user_name'] = $userinfo['user_name'].rand(1000, 9999); // 重名处理
 }

进行了重名检查。跑去网站后台一看,

Snap3
登陆多少次就产生多少用户名!

接着往下走 model('Users')->third_reg($userinfo);,这个就是第三方登录的处理代码了。
这个函数位于\mobile\include\apps\default\model\UsersModel.class.php,和get_one_user紧挨在一起。
代码如下

function third_reg($info) {
$username = $info['user_name'];
$password = time();
$email = $info['email'];
if ($this->register($username, $password, $email) !== false) {
// 更新附表
$this->table = "touch_user_info";
$touch_data['user_id'] = $uid;
$touch_data['aite_id'] = $info['aite_id'];
$this->insert($touch_data);
return true;
} else {
return false;
}
}

register函数是去添加ecshop的用户,下面的代码是添加ectouch的用户。$touch_data['user_id'] = $uid;这个代码就是为何ectouch的touch_user_info的user_id为何全是0的原因,因为 $uid根本没有赋值!搜索了全站关于 $uid的赋值语句,没有找到与用户登录有关的函数。所以这里有一个简单办法解决这个qq登陆的问题。就像我前面说的 if ($userinfo['user_name'] = model('Users')->get_one_user($userinfo['aite_id'])) {这个代码中get_one_user返回的数据为空就是因为这个ectouche的user_id为0,而ecshop是有正确user_id的,因为不相等,所以拿不到结果,如果我们修正ectouch这里的user_id值,使得get_one_user有返回,那么这个if就可以成立了。
后面的代码

 
                       self::$user->set_session($userinfo['user_name']);
                        self::$user->set_cookie($userinfo['user_name']);
                        model('Users')->update_user_info();
                        model('Users')->recalculate_price();
                        $jump_url = empty($this->back_act) ? url('index') : $this->back_act;
                        $this->redirect($jump_url);

就是程序处理登陆成功的代码。

跟踪一下注册程序,在mobile\include\apps\default\model\UsersModel.class.phpfunction third_reg中注册用户的代码是由register($username, $password, $email) 来执行的,在register函数中我们可以看到这样的代码

if (!ECTouch::user()->add_user($username, $password, $email)) {
if (ECTouch::user()->error == ERR_INVALID_USERNAME) {
ECTouch::err()->add(sprintf(L('username_invalid'), $username));
} elseif (ECTouch::user()->error == ERR_USERNAME_NOT_ALLOW) {
ECTouch::err()->add(sprintf(L('username_not_allow'), $username));
} elseif (ECTouch::user()->error == ERR_USERNAME_EXISTS) {
ECTouch::err()->add(sprintf(L('username_exist'), $username));
} elseif (ECTouch::user()->error == ERR_INVALID_EMAIL) {
ECTouch::err()->add(sprintf(L('email_invalid'), $email));
} elseif (ECTouch::user()->error == ERR_EMAIL_NOT_ALLOW) {
ECTouch::err()->add(sprintf(L('email_not_allow'), $email));
} elseif (ECTouch::user()->error == ERR_EMAIL_EXISTS) {
ECTouch::err()->add(sprintf(L('email_exist'), $email));
} else {
ECTouch::err()->add('UNKNOWN ERROR!');
}
//注册失败
return false;
} else {
//注册成功
/* 设置成登录状态 */
ECTouch::user()->set_session($username);
ECTouch::user()->set_cookie($username);

即如果注册成功之后将设置session和cookie,这个代码中的add_user函数和set_session函数位于mobile\plugins\integrates\integrate.php

function set_session($username = '')
{
if (empty($username)) {
ECTouch::sess()->destroy_session();
} else {
$sql = "SELECT user_id, password, email FROM " . $this->db->pre . 'users ' . " WHERE user_name='$username' LIMIT 1";
$row = $this->db->getRow($sql);

if ($row) {
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['user_name'] = $username;
$_SESSION['email'] = $row['email'];
}
}
}

可以看到在这里,程序将user_id赋值给了$_SESSION[‘user_id’] ,那么修正我们这个问题也就简单了。在function third_reg($info)函数中将$_SESSION[“user_id”]赋值给$touch_data[‘user_id’] 就能获取正确的user_id了,即$touch_data['user_id'] = $_SESSION["user_id"];

来到这里,我突然想,或许 if ($userinfo['user_name'] = model('Users')->get_one_user($userinfo['aite_id'])) 这样的判断是作者的本意,他本来就是想这样设计的,判断是否已经存在用户同时赋值给$userinfo[‘user_name’]。

所有的麻烦均来自$touch_data[‘user_id’] = $uid;这一个错误,如果当初这个值是正确的,那么touch_user_info表存放ectouch的aite_id值,users存放电脑的aite_id值,get_one_user联合查询两个表的情况,现在因为user_id错误,联合查询没有结果,这样将导致touch_user_info和users表中的数据不同步,用户查询判断失误,导致在users表中不断添加新用户。有两个方案可以修正这个问题,一个方案是根据users表中aite_id对应的user_id的值来修复touch_user_info的user_id,aite_id相等就给touch_user_info的user_id赋值;另外一个方案是以users表的aite_id为准来查询,将ectouch新登陆的aite_id值也写入users表,也可以将touch_user_info的aite_id迁移到users中来,就看有没有那个必要了。我采用了第二种方案。

今天太晚了,下次才能介绍怎样将手机登录的aite_id写入users表了,同时在手机的网站用户界面显示当前登录qq用户的昵称和头像,是不是会使网站显的更加亲和?欲知后事如何,请看百蔬君下回分解《同步ecshop与ectouch第三方QQ登陆并显示用户QQ昵称和头像》。

转载请注明:百蔬君 » 【原创文章】追查Ectouch QQ登录每次用户名都不同的原因和解决办法

喜欢 (14)or分享 (0)
发表我的评论
取消评论

请证明您不是机器人(^v^):

表情