rinald_未来往事

Discuz UCenter通信原理与数据自动同步免用户激活

Discuz
Discuz UCenter通信原理与用户免激活同步登录
本文阐述了Discuz UCenter用户中心数据自动同步其他通信应用 免去用户激活 批量激活站点整合用户相关的原理与实现方法。

最后更新:2013/04/08
新版本可以论坛程序后台——ucenter中设置直接激活即可(但不能数据同步)。


本文档基于DiscuzX2.0环境所写。原则上兼容X1.5/X2.5版本的Discuz!X,毕竟这里本人未对其他版本进行测试,如用于X2.0之外的其他版本,请谨慎操作,以免造成不可能挽回的结果。

Ucenter通信原理:
1、用户登录bbs,通过logging.php文件中,使用函数uc_user_login验证,如果验证成功,将调用函数uc_user_synlogin(位于uc_client下的client.php文件中), 在这个函数中调用 uc_api_post('user', 'synlogin', array('uid'=>$uid));之后向UC_API.'/index.php'传递了数据;这里的UC_API就是在 config.inc.php中的定义的uc_server之URL地址

2、uc_server的index.php接受参数数据,获得model为user,action为synlogin,就调用control目录下的 user.php类中的onsynlogin方法,通过foreach循环,以javascript的方式通知uc应用列表中的应用同步登录;即通过 get方式传递给应用目录中api下的uc.php一些数据;

3、uc.php接收通知并处理get过来的数据,并在函数synlogin(位于uc.php中)通过函数_authcode加密数据(默认以UC_KEY作为密钥),用函数_setcookie设置cookie;

4、各个应用在适当的文件中用对应的密钥解码上面设置的cookie,得到用户id等数据;通过这个值来判断用户是否经过其它应用登录过;
点击在新窗口中浏览此图片

以discuz举例:
一、用户登录检查与用户登录验证logging.php
在bbs的logging.php中如下代码段
  1. elseif($action == 'login') {  
  2.   
  3.  if($discuz_uid) {  
  4.   
  5.     $ucsynlogin = '';  
  6.   
  7.     showmessage('login_succeed'$indexname);  
  8.   
  9.  }  

检查用户id变量$discuz_uid是否为空来判断,用户是否登录(包括从别的应用登录。)
如果用户从bbs登录,则在登录验证成功后通过如下代码:
$ucsynlogin = $allowsynlogin ? uc_user_synlogin($discuz_uid) : '';

通知其它应用----“用户已从bbs登录,请通知其它应用设置cookie”
(uc_server通过javascript调用方式向其它应用的api/uc.php传递数据)
可以在uc应用目录下新建一个名为test.php的文件,来模拟登录成功,请求uc_server通知其它应用。文件内容为:
  1. < ?php  
  2.   
  3.  include_once "config.inc.php";  
  4.   
  5.  include_once "./uc_client/client.php";  
  6.   
  7.  echo uc_user_synlogin(1);  
  8.   
  9.  echo "<pre>";  
  10.   
  11.  var_dump($_COOKIE);  
  12.   
  13.  echo "</pre>";  
  14.   
  15.  ?>  
  16.    
  17. <script type="text/javascript">  
  18.   
  19.  var obj=document.getElementsByTagName("script");  
  20.   
  21.  for(var i=0;i<obj.length-1;i++) {  
  22.   
  23.     document.write("<a href=\""+obj.src+"\">"+obj.src+"</a><hr>");  
  24.   
  25.  }  
  26.   
  27.  </script>  

PS:这段测试代码还可以测试同步登录不好使的情况,具体使用方法,你可以思考一下(本文后面也有介绍),有问题可以在此文结尾发表评论与我讨论。
运行后,查看源代码即可看到javascript;
这里要注意了:这些javascript的通知中是不包含用户登录的应用的。也就是说只"通知"用户未登录的应用,因为用户通过uc_server登录成功的当前应用,当然不需要uc_server再通知了。具体代码请参看:webroot\uc_server\control\user.php中的onsynlogin函数的这句:
if($app['synlogin'] && $app['appid'] != $this->app['appid'])
代码解释:
$app['synlogin']是uc应用是否允许同步登录
而且应用id不等于用户当前登录的应用id
$app数组就是uc_server\data\cache\apps.php中的数组$_CACHE['apps'];
$this->app就是用户登录的应用

二、接受其它应用的同步登录通知:
在discuz的api目录下的uc.php中的函数synlogin,在这里接受uc_server发送过来的“同步登录通知”,并设置discuz的cookie,在这个函数中你可以查看到cookie的加密密钥的“算法”;
如果你想看看uc_server发送过的的“通知”是什么数据,你可以这么做:
1、修改要接受通知的应用目录下的api\uc.php,在$action = $get['action'];代码下面添加如下代码:
echo "<pre>";var_dump($get);echo "</pre>";die("<hr>api\uc.php");

2、将上面建立的test.php文件放置在其它允许同步登录的应用目录下,并在浏览器中运行,然后点击页面中对应第一步的应用链接,即可看到uc_server“通知”给改应用的数据;
  1. function synlogin($get$post)  
  2.   
  3. 在这个函数中通过_authcode函数,以密钥$discuz_auth_key加密了cookie;  
  4.   
  5. 在这里为了避免cookie名称冲突,在cookie名称(一般为:auth)前加了前缀($cookiepre),这个前缀也就是在config.inc.php中设置的那个cookie前缀值;  
  6.   
  7. 请看设置cookie的函数_setcookie:  
  8.   
  9. (通过参数$prefix来判断是否对cookie名称添加前缀$cookiepre)  
  10.   
  11. function _setcookie($var$value$life = 0, $prefix = 1) {  
  12.   
  13. lobal $cookiepre$cookiedomain$cookiepath$timestamp$_SERVER;  
  14.   
  15. setcookie(($prefix ? $cookiepre : '').$var$value,  
  16.   
  17. $life ? $timestamp + $life : 0, $cookiepath,  
  18.   
  19. $cookiedomain$_SERVER['SERVER_PORT'] == 443 ? 1 : 0);  
  20.   
  21.  }  
  22.   
  23. --------------------------------------------------------------------------  
  24. 密钥“算法”:   
  25.   
  26. $discuz_auth_key= md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);  
  27.   
  28. 也就是不同用户加密cookie的密钥可能不同;  

三、检查用户是否已登录(无论是那个应用下登录):
discuz的include目录中common.inc.php中有这样的代码:
  1. $discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);  
  2.   
  3. list($discuz_pw$discuz_secques$discuz_uid) =  
  4.   
  5. emptyempty($_DCOOKIE['auth']) ? array('''', 0) : daddslashes(explode("\t", authcode($_DCOOKIE['auth'], 'DECODE')), 1);  

这段代码就是解码在uc.php中用密钥($discuz_auth_key)加密的cookie值,以获得用户id($discuz_uid)
这里的解密函数位于bbs\include\global.func.php中,虽然未给函数传递cookie密钥,但函数中通过全局变量$GLOBALS['discuz_auth_key'])获得密钥。

Ucenter App通信详细过程如图:
点击在新窗口中浏览此图片





DiscuzX2.0 论坛UC用户数据自动同步其他应用,免其他站点用户激活修改方法
修改文件:/api/uc.php
  1. function synlogin($get$post) {   
  2.     global $_G;       
  3.       
  4.     if(!API_SYNLOGIN) {      
  5.        
  6.         return API_RETURN_FORBIDDEN;    
  7.              
  8.     }  
  9.        
  10.     header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');  
  11.        
  12.     $cookietime = 31536000;  
  13.        
  14.     $uid = intval($get['uid']);  
  15.        
  16.     $query = DB::query("SELECT uid, username, password FROM ".DB::table('common_member')." WHERE uid='$uid'");  
  17.        
  18.     if($member = DB::fetch($query)) {  
  19.        
  20.     dsetcookie('auth', authcode("$member[password]\t$member[uid]"'ENCODE'), $cookietime);  
  21.        
  22.     }   
  23. }  

修改该函数定义如下:
  1. function synlogin($get$post) {  
  2.    
  3. global $_G;  
  4.    
  5.   
  6. if(!API_SYNLOGIN) {  
  7.    
  8. return API_RETURN_FORBIDDEN;  
  9.    
  10. }  
  11.    
  12. header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');  
  13.    
  14. $cookietime = 31536000;  
  15.    
  16. $uid = intval($get['uid']);  
  17.    
  18. $query = DB::query("SELECT uid, username, password FROM ".DB::table('common_member')." WHERE uid='$uid'");  
  19.    
  20. if($member = DB::fetch($query)) {  
  21.    
  22. dsetcookie('auth', authcode("$member[password]\t$member[uid]"'ENCODE'), $cookietime);  
  23.    
  24. }else{  
  25.    
  26. $username = $get['username'];   
  27. $password = md5(time().rand(100000, 999999));  
  28.    
  29. $email = $get['email'];  
  30.    
  31. $ip = $_SERVER['REMOTE_ADDR'];  
  32.    
  33. $time = time();   
  34. $userdata = array(  
  35.    
  36. 'uid' => $uid,  
  37.    
  38. 'username' => $username,  
  39.    
  40. 'password' => $password,  
  41.    
  42. 'email' => $email,  
  43.    
  44. 'adminid' => 0,  
  45.    
  46. 'groupid' => 10,  
  47.    
  48. 'regdate' => $time,  
  49.    
  50. 'credits' => 0,  
  51.    
  52. 'timeoffset' => 9999  
  53.    
  54. );  
  55.    
  56. DB::insert('common_member'$userdata);  
  57.    
  58. $status_data = array(  
  59.    
  60. 'uid' => $uid,  
  61.    
  62. 'regip' => $ip,  
  63.    
  64. 'lastip' => $ip,  
  65.    
  66. 'lastvisit' => $time,  
  67.    
  68. 'lastactivity' => $time,  
  69.    
  70. 'lastpost' => 0,  
  71.    
  72. 'lastsendmail' => 0,  
  73.    
  74. );  
  75.    
  76. DB::insert('common_member_status'$status_data);  
  77.    
  78. DB::insert('common_member_profile'array('uid' => $uid));  
  79.    
  80. DB::insert('common_member_field_forum'array('uid' => $uid));  
  81.    
  82. DB::insert('common_member_field_home'array('uid' => $uid));  
  83.    
  84. DB::insert('common_member_count'array('uid' => $uid));   
  85. $query = DB::query("SELECT uid, username, password FROM ".DB::table('common_member')." WHERE uid='$uid'");  
  86.    
  87. if($member = DB::fetch($query)) {  
  88.    
  89. dsetcookie('auth', authcode("$member[password]\t$member[uid]"'ENCODE'), $cookietime);  
  90.    
  91. }  
  92.    
  93. }  
  94.    
  95. }  
原文作者:rinald
原文地址:http://fity.cn/post/238.html
互联网技术更新较快,本站很多文章具有实效性,我会及时更新原文,但转载的文章无法通知更新。为了不给读者造成困惑或误导,请您在转载时保留此出处信息,尊重别人也是尊重自己。

已有6位网友发表了看法:

1L海天 2012-08-27 18:01:16 回复
X2.5 貌似不行啊?
1Lrinald 2012-08-28 12:22:51 回复
@海天 你测试过了吗?这篇文档是基于X2写的,当时为了解决X2多站点用户激活问题写的。X2.5未经过测试。
2Lzpl89898989 2015-01-27 16:13:33 回复
很好,能用!解决了我的问题。
自动激活
谢谢啊大神
2Lrinald 2016-08-11 10:01:00 回复
@zpl89898989 对你有所帮助就好,欢迎常来~~
3Lhwq1234 2016-08-10 09:50:06 回复
ucenter可以进行数据同步吗,a项目的user表和b项目的user表部分数据(用户名,密码,邮箱一致),当a项目user表的用户名密码或邮箱修改时,b项目对应的也会进行修改,反之亦然。ucenter能够做到吗?
3Lrinald 2016-08-11 09:59:26 回复
@hwq1234 欢迎来访未来往事博客。如果是多论坛可以基于ucenter进行整合;如果是uc与其他程序项目可以使用uc api进行开发接入。

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。