安装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/321a7b99f69f66026
来至: http://hi.baidu.com/fangshawn/blog/item/fb48ef8ba26e0db80f2444b5.html