discuz 同步登录 代码浅析 [转载]

安装ucenter;

安装discuz,自动加载到ucenter并通信成功;

再尝试把ucenter里的例子部署到ucenter通信,叫uclogion吧,想从uclogion登录,discuz会同步登录,

直接把config.inc.php里的////通信相关全部注释,把ucenter里生成的"应用的 UCenter 配置信息"加到config.inc.php后面;通信不成功,别急;参考下 http://www.discuz.net/thread-1388614-1-1.html

出错虽然不是在同一地方,但给我帮助很大了,

最后结果;是config.inc.php加个$database = 'mysql';就通信成功;



好了;环境有了,开始我们的代码分板吧!

在ucenter里的登录页面写的很清楚

           //生成同步登录的代码
              $ucsynlogin = uc_user_synlogin($uid);          //$uid 会员信息的ID;

echo一下;居然什么也没打印出来,别急,右击看下源文件,一串JS;

基本原理明白了:是uclogion里的JS调用discuz的接口生成跨域的cookie;实现登录登录;

当然当有多个应用需要同步时会生成多串JS去调用不同的应用接口生成相应跨域的cookie;

继续按住crul 把相关函数找出来;

在uclogion/uc_client/client.php里的

function uc_user_synlogin($uid) {
$uid = intval($uid);
$return = uc_api_post('user', 'synlogin', array('uid'=>$uid));
return $return;
}

/**
*
*/
function uc_api_post($module, $action, $arg = array()) {
$s = $sep = '';
foreach($arg as $k => $v) {
   $k = urlencode($k);
   if(is_array($v)) {
    $s2 = $sep2 = '';
    foreach($v as $k2 => $v2) {
     $k2 = urlencode($k2);
     $s2 .= "$sep2{$k}[$k2]=".urlencode(uc_stripslashes($v2));
     $sep2 = '&';
    }
    $s .= $sep.$s2;
   } else {
    $s .= "$sep$k=".urlencode(uc_stripslashes($v));
   }
   $sep = '&';
}
$postdata = uc_api_requestdata($module, $action, $s);
return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);
}

//加密post数据;在同步时也就用UC_ket加密,再在ucenter解密
function uc_api_input($data) {
$s = urlencode(uc_authcode($data.'&agent='.md5($_SERVER['HTTP_USER_AGENT'])."&time=".time(), 'ENCODE', UC_KEY));
return $s;
}

function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {

//限制请求次数;
$__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;
if($__times__ > 2) {
   return '';
}
/////
$url .= (strpos($url, '?') === FALSE ? '?' : '&')."__times__=$__times__";


return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block);
}

.........................................................................................................................................................
最后追踪到就是个核心的发起请求的函数了;发起请求的函数用的是fsockopen;
最后数据是怎样post过去的,也就是$out这个变量内容起的作用了;

简单点的就是知道是post到哪个地址去;我们echo下$url得到http://127.0.0.1/ucenter/index.php?__times__=1

/**
* 发起socket请求,返回请求结果;</p>
* 在同步时请求结果一般是N串script字符串,
* 用于种下各应用cookie实现同步登录
*/
function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {
$return = '';
$matches = parse_url($url);
!isset($matches['host']) && $matches['host'] = '';
!isset($matches['path']) && $matches['path'] = '';
!isset($matches['query']) && $matches['query'] = '';
!isset($matches['port']) && $matches['port'] = '';
$host = $matches['host'];
$path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/';
$port = !empty($matches['port']) ? $matches['port'] : 80;
if($post) {
   $out = "POST $path HTTP/1.0\r\n";
   $out .= "Accept: */*\r\n";
   //$out .= "Referer: $boardurl\r\n";
   $out .= "Accept-Language: zh-cn\r\n";
   $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
   $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
   $out .= "Host: $host\r\n";
   $out .= 'Content-Length: '.strlen($post)."\r\n";
   $out .= "Connection: Close\r\n";
   $out .= "Cache-Control: no-cache\r\n";
   $out .= "Cookie: $cookie\r\n\r\n";
   $out .= $post;
} else {
   $out = "GET $path HTTP/1.0\r\n";
   $out .= "Accept: */*\r\n";
   //$out .= "Referer: $boardurl\r\n";
   $out .= "Accept-Language: zh-cn\r\n";
   $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
   $out .= "Host: $host\r\n";
   $out .= "Connection: Close\r\n";
   $out .= "Cookie: $cookie\r\n\r\n";
}
$fp = @fsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout);// 打完链接;发送数据;
if(!$fp) {
   return '';
} else {
   stream_set_blocking($fp, $block);
   stream_set_timeout($fp, $timeout);
   @fwrite($fp, $out);
   $status = stream_get_meta_data($fp);
   if(!$status['timed_out']) {
    while (!feof($fp)) {
     if(($header = @fgets($fp)) && ($header == "\r\n" || $header == "\n")) {
      break;
     }
    }

    $stop = false;
    while(!feof($fp) && !$stop) {
     $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
     $return .= $data;
     if($limit) {
      $limit -= strlen($data);
      $stop = $limit <= 0;
     }
    }
   }
 
    $return;

   @fclose($fp);
   return $return;
}
}

上面是在uclogion里也就是第一个应用登录时怎样调用ucenter的接口相关的函数
-------------------------------------------------
在上面echo $url http://127.0.0.1/ucenter/index.php?__times__=1

我们直接找到ucenter的index.php

get过来的参数明显是?__times__=1;那么还有post过来的参数;我们直接在入口处就把他print一下;
echo "<pre>";
print_r($_POST);

再一次在uclogion是尝试登录;得到这

Array
(
    [m] => user
    [a] => synlogin
    [inajax] => 2
    [release] => 20090121
    [input] => 2e26ADne+3AM5QckfdoMJiGhwpzfoM9xQOqmCsbT0p+d8Y/uMSpKjP2CJk3/2HpMsAnfMuiTjNAG+YLOHQD2uMf+b/VQbKOqGKy7Qmbah5aKKsHilzyQepU
    [appid] => 2
)
结合下上面的uc_api_requestdata函数,基本上知道各个参数的函义
m是执行的模块,a是执行的方法; release是cookie的过期时间; input 明显是登录用户的数据,其实也就是用户的uid过来的;appid是提交应用的id;

在ucenter里的index.php里有;
   $classname = $m.'control';
$control = new $classname();
$method = 'on'.$a;
把上面的echo出来;也就明白了你在调用了那个类跟方法了:
//同步类名usercontrol 方法onsynlogin
在index.php最后返回是下面;也就是对面的JS代码;
echo $data = $control->$method();

function onsynlogin() {
   $this->init_input();
    $uid = $this->input('uid');

   if($this->app['synlogin']) {

    if($this->user = $_ENV['user']->get_user_by_uid($uid)) {
   
     $synstr = '';
     //遍历应用生成对应的script
     foreach($this->cache['apps'] as $appid => $app) {
      if($app['synlogin'] && $app['appid'] != $this->app['appid']) {
       $synstr .= '<script type="text/javascript" src="'.$app['url'].'/api/uc.php?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1"></script>';
      }
     }
     return $synstr;
    }
   }
   return '';
}
以后是ucenter各个应用登录后怎样生成其它对应的的JS(或许取个名字好理解点:叫"JScookie通行证"吧);
ulogion登录后同时过ucente获得jscookie通行证去同步登录其它ucenter的注册应用;
---------------------------------------------------------------------------------
再来观察下那段JScookie通行证吧;
<script type="text/javascript" src="http://127.0.0.1/bbs/api/uc.php?time=1260777552&code=0105TuJc7mAGVHAKhj%2FsfrNkPxwsLBBqOZcqBkRx0mZmEtPCKYGfZ32nog3gmS1yc6hqsyqD8FgxsoKfXJE2QkygQT3%2F25aZ%2BnuMD5ryzjZWLOSeIAYXgGgYOJHHRYosz458eo06bPSI1cAmb6Dgx8THR0u9mNpdHGdi3%2BsiHw" reload="1"></script>
知道了是接口文件是discuz的api/uc.php下;

ctrl+f找一下 synlogin 很快找到180行 elseif($action == 'synlogin') {

里面的核心代码也就是
header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
这个头信息发送后就强制了浏览器允许跨域注册cookie;
紧接着是像我们平时setcookie一样;discuz 封装了下个dsetcookie来setcookie;

大家可以找下相关的跨域setcookie的资料看看;
关于同步登录的相关文章还有这篇,说的比我好多了,有图说明;http://hi.baidu.com/e_polo/blog/item/321a7b99f69f66026e068c8c.html

猜你喜欢

转载自qianxunniao.iteye.com/blog/1596232