分类: Ucenter

phpcms V9 整合 Discuz! X1.5

2011 年 04 月 12 日 at 上午 11:45分类:PHP | PHPCMS | Ucenter | WEB开发

首先把原理大致的描述一下,我们把phpsso作为uc的一个应用来处理。UCenter 作服务端;phpsso 与 Discuz! 分别作 UCenter 的客户端应用;phpsso 与 Discuz! 通过 UCenter 发生交互。phpcms 通过 phpsso 与 Discuz! 发生交互。
从 phpcms 注册后,同步注册到 UCenter (这个过程需要在后台开启uc,并且保证通信成功才行),这是因为 phpcms 实际上是与 UCenter 进行通信。用该帐号从 Discuz! 登录,实际上是从 UCenter 登录。而在 Discuz! 中,是没有这个帐号的,所以要把 UCenter 中的这个帐号信息注册到 Discuz! 中(即所谓的“激活”)。这是康盛创想设计的 UCenter 与 Discuz! 帐号转移机制,我们是无法改变的(除非把 Discuz! 从 UCenter 中分离!)反之,从 Discuz! 注册后,同步注册到 phpsso 。但 phpsso 同时注册到 phpcms ,所以就不需要“激活”了。

第一步:安装 phpcms V9 。全新安装 Discuz! X1.5 (同时安装 UCenter)。

第二步:在 UCenter 中添加“phpsso 应用”。
1、管理员登录 Discuz! X1.5 管理中心,进入“UCenter”,点击“应用管理”。
2、点击“添加新应用”按钮,选择安装方式为”自定义安装“。在展开的”添加新应用“参数配置表中,各项参数配置如下:
应用名称:phpsso
应用的主 URL:http://localhost/phpcms/phpsso_server (必填,最后不要带斜线)
应用 IP:(选填,正常情况下留空即可。如果由于域名解析问题导致 UCenter 与该应用通信失败,请尝试设置为该应用所在服务器的 IP 地址。)
通信密钥:(必填,phpcms 的通信密钥必须与此设置保持一致,否则 phpsso 将无法与 UCenter 正常通信。)
应用类型:其它(必选)
应用的物理路径:(选填,默认留空)
查看个人资料页面地址:(选填,URL中域名后面的部分,如:/space.php?uid=%s 这里的 %s 代表uid)
应用接口文件名称:(选填,默认为uc.php)
标签单条显示模板:(选填,默认留空)
标签模板标记说明:(选填,默认留空)
是否开启同步登录:是(可选,开启同步登录后,当用户在登录 Discuz! 时,同时也会登录 phpcms 。)
是否接受通知:是(可选)

3、提交后,生成新的应用ID。(记住这个应用ID,在”第三步“中将用到它。)

第三步:配置 phpsso 。
1、管理员登录 phpcms V9 后台管理中心,进入“phpsso”,点击“系统设置”。
2、在“系统设置”下的“UCenter配置”一栏中,各项参数配置如下:
是否启用:是
Ucenter api 地址:http://localhost/ucenter (必填,最后不要带斜线)
Ucenter api IP:(选填,一般不用填写,遇到无法同步时,请填写 UCenter 主机的IP地址)
Ucenter 数据库主机名:localhost (必填,视实际情况而定)
Ucenter 数据库用户名:root (必填,视实际情况而定)
Ucenter 数据库密码:root (视实际情况而定)
Ucenter 数据库名:discuz (必填,视实际情况而定)
Ucenter 数据库表前缀:dz_ucenter_ (必填,视实际情况而定。如果此项填写错误,将导致 phpcms 无法注册新会员!)
Ucenter 数据库字符集:UTF-8 (必选,视实际情况而定)
应用id(APP ID):(必填,该值来在“第二步”中 UCenter 创建的 phpsso 应用时自动生成。)
Ucenter 通信密钥:(必填,一定确保该值与在“第二步”中 UCenter 创建的 phpsso 应用密钥相同。)
3、提交。

第四步:查看通信状态。
查看在 UCenter 中创建的 phpsso 应用与 UCenter 通信是否成功。
如果通信成功,则进行下一步。
如果通信失败,请检查“第二步”与“第三步”中的各项参数配置是否正确。

第五步:修改 Discuz! 会员登录相关
1、修改 template\default\member\login.htm 第 51 行,删除
ajaxpost(‘loginform_$loginhash’, ‘returnmessage_$loginhash’, ‘returnmessage_$loginhash’, ‘onerror’);return false;
2、修改 source\function\function_core.php 第 1458 行,查找
$param['header'] = true;
替换为:
$param['header'] = false;
3、修改 template\default\member\login_s imple.htm 第 2 行,删除
onsubmit=”return lsSubmit()”

常见问题解答:
1、phpsso 整合 UCenter 后,Discuz! 注册会员在 phpcms 中为什么不显示昵称?
答:这是因为 Discuz! 只有“用户名”,没有“昵称”的概念。要解决这个问题,可以把 Discuz! 的“用户名”做为 phpcms 的“昵称”来使用。
打开 api/phpsso.php ,在第 41 行(代码为:$userinfo['password'] = isset($arr['password']) ? $arr['password'] : exit(’0′);)的下面添加如下语句:
$userinfo['nickname'] = isset($arr['username']) ? $arr['username'] : exit(’0′);
这样修改以后,在 Discuz! 新注册的会员,在 phpcms 中就可以显示昵称了。
但修改以前的会员仍然是没有“昵称”的。这就需要在数据库中修改了。

UCENTER和PHPSSO的异同点

没有评论

2011 年 04 月 03 日 at 下午 9:18分类:PHP | PHPCMS | Ucenter

之前研究过ucenter,感觉写的很不错。里面的很多的东西我们可以直接拿来用,可以在我们建站的时候直接拿来用。但是今天我看了phpcms的phpsso之后,感觉UC和PS都还不错。phpcms的phpsso和ucenter的功能是一样的也是一个用户处理中心,只是phpsso现在的功能还没有ucneter那么多。在这里就两者的一些异同进行简单的分析下:
我们都知道,在要处理类似与同步登录和同步退出时,都会用到UC这样子的东西(当然这也不一定)。UC在处理同步登录和同步退出的过程在本博客已经讲解过了,这里就不解说了。简单说下,就是客户端向用用户名(或者其他)和密码还有加密密钥组成的加密字符通过套接字发送给服务器端,然后服务器获取这些加密字段后,进行解密,然后在处理相关的操作,之后返回的是一串JS串。其实PS(phpsso)在处理同步登录和同步退出时和UC采用的方法是一样的。都是通过用户名和密码(当然还有操作信息)加密之后组成的字符串通过套接字发送到服务器端,然后返回的也是JS串。在同步方面这两个采用的方法原理是完全一样的。只是实现的过程或者是方法有点不同罢了。
当我们向UC发送一些非同步方面的消息时,比如修改积分时,那么UC 是怎么处理的呢?
当我们客户端向用户发送这个信息时,服务端会调用文件credit.php文件里面的onrequest方法来处理。这里的处理大致是这样子的:方法一:如果$toapp['extra']['apppath'](应用物理路径,就是相对应UC的路径)这个变量为false的话,首先通过传递过来的数据处理本地(服务器段的)数据库里面的数据,然后通过include加载,记住这里使用的是加载客户端的文件(api/uc.php),这样子来处理客户端方面数据库中需要处理的信息。方法二:如果$toapp['extra']['apppath']为true的话,那么会通过model下面的note.php文件的get_url_code方法组建URL,然后通过model下面的misc.php文件中的dfopen方法,该方法是通过套接字将数据传输到客户端,然后有客户端自己处理自己需要处理的数据。
PS中采用同样的方法获取到传输过来的数据,然后经过处理之后,但是PS只是采用了一种方法。首先对本地的一些需要处理的数据库进行处理,然后会调用messagequeue.class.php中的notice方法,而该方法调用函数ps_send()来处理,这个函数最终使用的套接字来将数据又传送到客户端的api/下面的文件中,还是通过客户端来处理客户端需要修改的的数据。
以上都是个人的观点,还望各位大侠提意见。。。

Ucenter中跨域访问的分析

没有评论

2011 年 03 月 17 日 at 下午 3:17分类:PHP | Ucenter | WEB开发

记得在之前的一篇名为《UCENTER 会员同步登录通讯原理》的文章中,我说到了,在所有的应用中要想向Ucenter的服务器端发送数据,都调用到了一个函数(uc_api_post)。其实这个函数里面的学问蛮大的。他涉及的东西很多,我们一一来分析:
ucenter客户端向服务器端发送请求的原理解析:
当我们一个应用(UCHome)向Ucenter发送一个同步登陆或者同步退出时,该应用(UCHome)是怎么想用
服务器端发送数据请求的呢?
以同步登陆为例,当我们调用函数uc_user_synlogin(),此函数调用函数uc_api_post(),并且传递相关的mod的值和action的值,我们通过找到该函数知道,此函数调用的有两个函数:uc_api_requestdata()和uc_fopen2(),函数uc_api_requestdata是组织一些带参数的URL形式,函数uc_fopen2()最终调用uc_fopen()通过fsockopen()套接字实现打开远程文件的功能。
这个过程中最主要的功能就是这个套接字的使用了。我们来看下这个函数:

function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {
	$return = '';
	$matches = parse_url($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;
        //预处理要写入远程文件的HEAD头信息和参数$post;
	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";
	}
         //使用fsockopen打开远程连接
	$fp = @fsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout);
	if(!$fp) {
		return '';//note $errstr : $errno \r\n
	} else {
		stream_set_blocking($fp, $block);//让程序无阻塞
		stream_set_timeout($fp, $timeout);   //超时设置
		@fwrite($fp, $out);
		$status = stream_get_meta_data($fp);   //读取数据流
		if(!$status['timed_out']) {//判断是否超时(超时时该值为空,否则为1)
			while (!feof($fp)) {
				if(($header = @fgets($fp)) && ($header == "\r\n" ||  $header == "\n")) {
					break;
				}
			}

			$stop = false;
			while(!feof($fp) && !$stop) {
                                 //获取服务器端传输来的数据(这里为一些JS串)
				$data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
				$return .= $data;
				if($limit) {
					$limit -= strlen($data);
					$stop = $limit <= 0;
				}
			}
		}
		@fclose($fp);
		return $return;
	}
}

当客户端向服务器端请求时,会调用套接字发送head请求头信息,并且带上客户端发送给服务器端的数据,服务器接受这些数据之后经过一系列的处理之后返回一些JS字符串。这些字符串数据被打开的套接字fsockopen()接收并且存储在$return变量中,之后便是一步步的返回给最初调用的函数如:uc_user_synlogin(),这就是为什么我们把这个函数的返回值输出来是一串JS字符串了。
这样子 整个Ucenter的跨域访问就彻底结束了。。。

UCHome关于注册同步登陆的问题

一条评论

2011 年 03 月 07 日 at 下午 2:46分类:PHP | Ucenter | UCHome | WEB开发

在uchome中,当我们注册一个用户账号时,注册完毕之后,那么默认就是处在登陆状态的。
但是在uchome这个应用是处在登陆状态了,但是其他应用程序并不是处在登陆状态的。
也就是说uchome在注册的时候并不能实现注册时实现同步登陆的效果。
那么这个问题该怎么解决呢?其实也不是很难。
记得要实现同步登陆,那么就必须调用ucenter服务器端得接口函数uc_user_synlogin();
通过调用该函数产生的一些JS串来远程加载相关应用下面api/uc.php文件里面的相关函数,一次来设置cookie以实现同步登陆的问题。
那么在注册过程中我们也是可以调用该函数来执行同步登陆的效果的。
uchome中用户注册的时候,会先对一系列的数据进行校验,之后回调用ucenter的接口函数uc_user_register进行注册,
对其返回的结果进行相关的判断,如果注册成功了,那么接下来执行的是把用户的数据插入本地的member数据表中,之后在执行开通空间和加载默认好友等事情。然后还会把相关的信息写入session表中,同时设置COOKIE,以表示登陆了。
那么只要在这里插入我们想要的代码就行了;如下:

//设置cookie
ssetcookie('auth', authcode("$setarr[password]\t$setarr[uid]", 'ENCODE'), 2592000);
ssetcookie('loginuser', $username, 31536000);
ssetcookie('_refer', '');

//调用ucenter接口函数的同步登录函数
$ucsynlogin = uc_user_synlogin($newuid);

然后我们修改do_reister.php文件中的205行左右的代码:

showmessage('registered', $jumpurl);

修改为:

showmessage('registered', $jumpurl,1,array($ucsynlogin));

那么这样子,我们要的实现效果就可以解决了!

通过设置P3P头来实现跨域访问COOKIE – PHP技术

没有评论

2011 年 03 月 01 日 at 上午 10:35分类:PHP | Ucenter | UCHome | WEB开发

网上看了别人介绍的一片文章,说使用P3P可以完成跨域COOKIE操作,感觉很COOL,不过没有提供源代码,我胡乱写了一下,大家看看。
实际工作中,类似这样的要求很多,比如说,我们有两个域名,我们想实现在一个域名登录后,能自动完成另一个域名的登录,也就是PASSPORT的功能。
我只写一个大概,为了测试的方便,先编辑hosts文件,加入测试域名(C:\WINDOWS\system32\drivers\etc\hosts)
127.0.0.1www.a.com
127.0.0.1www.b.com
首先:创建a_setcookie.php文件,内容如下:

<?php
//header('P3P:CP="CURaADMaDEVaPSAoPSDoOURBUSUNIPURINTDEMSTAPRECOMNAVOTCNOIDSPCOR"');

setcookie("test",$_GET['id'],time()+3600,"/",".a.com");
?>

然后:创建a_getcookie.php文件,内容如下:

<?php
var_dump($_COOKIE);
?>

最后:创建b_setcookie.php文件,内容如下:

<scriptsrc="http://www.a.com/a_setcookie.php?id=www.b.com"></script>

----------------------------
三个文件创建完毕后,我们通过浏览器依次访问:

http://www.b.com/b_setcookie.php

http://www.a.com/a_getcookie.php

我们会发现,在访问b.com域的时候,我们并没有在a.com域设置上cookie值。
然后我们修改一下a_setcookie.php文件,去掉注释符号,a_setcookie.php即为:

<?php
header('P3P:CP="CURaADMaDEVaPSAoPSDoOURBUSUNIPURINTDEMSTAPRECOMNAVOTCNOIDSPCOR"');

setcookie("test",$_GET['id'],time()+3600,"/",".a.com");
?>

再次通过浏览器依次访问:

http://www.b.com/b_setcookie.php

http://www.a.com/a_getcookie.php

这次,你会发现在访问b.com域的时候,我们设置了a.com域的cookie值。
末了补充一句,似乎只有IE对跨域访问COOKIE限制比较严格,上述代码在FIREFOX下测试,即使不发送P3P头信息,也能成功。不过IE是老大啊。

利用UCENTER 实现自己的程序登录 ,UCHOME 同步登录

没有评论

2011 年 03 月 01 日 at 上午 10:30分类:PHP | Ucenter | UCHome | WEB开发

一直有这么一个 需求, 就是实现同步登录登出, 今天下午我简单的看了下 康盛 产品同步登录的 东西, 然后简单的写了个小的程序, 实现我的 程序登录 , UCHOME 的程序也可以登录;
主要的 就是借助UCENTER , 如果我的程序登录成功 ,获取 当前登录用户的 UID 然后通过
list($uid, $username, $password, $email) = uc_user_login($_POST['username'], $_POST['password']); 这段代码判断是否 能够登录, 如果返回 1 则表示登录成功。 若要 UCHOME 也跟着登录 则需要调用 $ucsynlogin = uc_user_synlogin($uid); ,这句话返回 一段 只要把这句话 成功的输入到页面上, JS 就会被触发, 然后把 用户名和密码发送到 UCHOME 去 验证, 就这么简单。 下面我把我 的 代码贴出来看看,….

include ‘./config.inc.php’;
include ‘./uc_client/client.php’;
$username = $_POST['username'];
$password = $_POST['password'];
list($uid, $username, $password, $email) = uc_user_login($_POST['username'], $_POST['password']);
if($uid>0) {
$ucsynlogin = uc_user_synlogin($uid);
echo($ucsynlogin);
setcookie(‘tarfly_auth’, uc_authcode($uid.”\t”.$username, ‘ENCODE’));
echo ‘你已经登录成功!退出‘;
}

Ucenter 会员同步登录通讯原理

没有评论

2011 年 02 月 28 日 at 下午 4:25分类:PHP | Ucenter | UCHome | WEB开发

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中如下代码段


} elseif($action == 'login') {

if($discuz_uid) {

$ucsynlogin = '';

showmessage('login_succeed', $indexname);

}

检查用户id变量$discuz_uid是否为空来判断,用户是否登录(包括从别的应用登录。)

如果用户从bbs登录,则在登录验证成功后通过如下代码:

$ucsynlogin = $allowsynlogin ? uc_user_synlogin($discuz_uid) : ”;

通知其它应用—-“用户已从bbs登录,请通知其它应用设置cookie”

(uc_server通过javascript调用方式向其它应用的api/uc.php传递数据)

可以在uc应用目录下新建一个名为test.php的文件,来模拟登录成功,请求uc_server通知其它应用。文件内容为:

<?php

include_once "config.inc.php";

include_once "./uc_client/client.php";

echo uc_user_synlogin(1);

echo "<pre>";

var_dump($_COOKIE);

echo "</pre>";

?>

<script type="text/javascript">

var obj=document.getElementsByTagName("script");

for(var i=0;i<obj.length-1;i++) {

document.write("<a href=\""+obj[i].src+"\">"+obj[i].src+"</a><hr>");

}

</script>

ps:这段测试代码还可以测试同步登录不好使的情况,具体使用方法,你可以思考一下(本文后面也有介绍),有问题可以在此文结尾发表评论与我讨论。

运行后,查看源代码即可看到javascript;
最主要的就是这段js代码串,通过这段js代码串去加载相应的应用api接口里面的uc.php文件,并且通过这串js代码传输了相关的数据到该文件中,以此来实现同步登陆的功能。
JS代码代码串如下:

这里要注意了:这些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“通知”给改应用的数据;

—————————分割线——————————-

function synlogin($get, $post)

在这个函数中通过_authcode函数,以密钥$discuz_auth_key加密了cookie;

在这里为了避免cookie名称冲突,在cookie名称(一般为:auth)前加了前缀($cookiepre),这个前缀也就是在config.inc.php中设置的那个cookie前缀值;

请看设置cookie的函数_setcookie:

(通过参数$prefix来判断是否对cookie名称添加前缀$cookiepre)

function _setcookie($var, $value, $life = 0, $prefix = 1) {

global $cookiepre, $cookiedomain, $cookiepath, $timestamp, $_SERVER;

setcookie(($prefix ? $cookiepre : '').$var, $value,

$life ? $timestamp + $life : 0, $cookiepath,

$cookiedomain, $_SERVER['SERVER_PORT'] == 443 ? 1 : 0);

}

密钥“算法”:

$discuz_auth_key= md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);

也就是不同用户加密cookie的密钥可能不同;

三、检查用户是否已登录(无论是那个应用下登录):

discuz的include目录中common.inc.php中有这样的代码:

$discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);

list($discuz_pw, $discuz_secques, $discuz_uid) = empty($_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'])获得密钥。