About 膘叔

纯粹一垃圾程序员

UCenter 与 Asp.net 通讯

原文来自于:http://www.cnblogs.com/dozer/archive/2010/09/21/ucenter-api_with_asp-net.html,作者:Dozer。

因为我一直是在做PHP版的通讯,因此有asp.net的也就贴出来,相信可以给更多人帮助。

前言

学生在线下面有多个子站,其中包括一个Discuz论坛
那当然要充分利用强大的UCenter来实现多点登陆
UCenter和别的网站跨域通讯,那用的肯定是WebService
PHP官方封装好了,所以很容易就搞定了,但是.Net…
网上找不到任何核心的通讯手册,除非去扣那个PHP的开发手册
Google后:
UCenter 接口开发手册:这个就是官方封装过的版本,其实这个根本不能叫接口开发手册,里面介绍的都是已经封装过的PHP函数,对核心只字不提
一个项目文件:刚看到这个我很兴奋,但是按照它说的调整好后,却不能用!一开始还不懂为什么,懂得原理后才知道,它其实是个空壳
一个DLL文件:这个DLL很强大,可惜,没有例程、没有手册、没有注释,还有BUG…
但是相对来说,最后的那个DLL是最接近的,所以决定反编译之,开始研究
最后就有了Dozer编辑版~ 成功实现通讯!

UCenter通讯原理

原理网上很多,我也只是知道一个皮毛,在这里就以同步登陆为例子,来讲解一下这个类库的用法

以上就是同步登陆的步骤,如果你已经配置好,那么只需要这3行代码

namespace WebApplication1
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var uc = new UCClient();
            var user = uc.UC_User_Login("user", "password").User;
            Response.Write(uc.UC_User_Synlogin(user.Uid));
        }
    }
}

总之,在你的网站程序中需要有2个部分
一个是UCAPI部分,需要给UCenter调用
另外一部分就是UClient部分,用于调用UCenter
这样才能在最后实现双向通讯

UCenter端配置过程

按照惯例,登陆UCenter后台,然后添加应用程序
别的地方和PHP网站一样,只有一个地方,就是“应用接口文件名称”的地方
需要填写uc.ashx
这个和此类库的原理有关,后面详解

提交…可惜,显示通讯不成功,因为我的网站还没配置呢~
提交后再次编辑,会得到一段PHP的配置信息

define('UC_CONNECT', 'mysql');
define('UC_DBHOST', localhost');
define('UC_DBUSER', 'root');
define('UC_DBPW', 'mysql');
define('UC_DBNAME', 'uc_discuz');
define('UC_DBCHARSET', 'utf8');
define('UC_DBTABLEPRE', '`uc_discuz`.uc_');
define('UC_DBCONNECT', '0');
define('UC_KEY', 'qwertyui');
define('UC_API', 'http://localhost/ucenter');
define('UC_CHARSET', 'utf-8');
define('UC_IP', '127.0.0.1');
define('UC_APPID', '19');
define('UC_PPP', '20');

先留着,后面会用到~

WebApplication端配置过程

第一步当然是Web.config文件

新建一个网站应用程序,并且引用这个类库
上面的配置信息是PHP用的,我们把它转换到Web.config文件中去

<appSettings>
 <add key="UC_CONNECT" value="mysql"/>
 <add key="UC_DBHOST" value="localhost"/>
 <add key="UC_DBUSER" value="root"/>
 <add key="UC_DBPW" value="mysql"/>
 <add key="UC_DBNAME" value="uc_discuz"/>
 <add key="UC_DBCHARSET" value="utf8"/>
 <add key="UC_APPIDUC_DBTABLEPRE" value="`uc_discuz`.uc_"/>
 <add key="UC_DBCONNECT" value="0"/>
 <add key="UC_KEY" value="qwertyui"/>
 <add key="UC_API" value="http://localhost/ucenter"/>
 <add key="UC_CHARSET" value="utf-8"/>
 <add key="UC_IP" value="127.0.0.1"/>
 <add key="UC_APPID" value="19"/>
 <add key="UC_PPP" value="20"/>
</appSettings>

第二步是建立UCAPI,供UCenter调用(这步完成后UCenter中会显示通讯正常)

1、新建一个叫API的文件
2、在里面建立一个新文件uc.ashx(不要建立.aspx)
3、打开这个uc.ashx文件,本来它继承于IHttpHandler,我们把它修改一下,让它继承于FS.API.UCenter.UCAPI.UCAPIBase
4、实现一下这个抽象类的函数(利用VS的代码自动完成功能)
5、接下来你就可以在这里写一些逻辑代码了
比如,当有人在别的站同步登陆后,会通知你的站点
然后会调用 Synlogin 函数,这时候,你就需要在这个函数里写一些代码
例如:写cookie之类的
Q:UCenter为什么不直接写Cookie,子站读Cookie?
A:UCenter实现的是跨域登陆,所以每个子站的Cookie是分开的,需要自己实现!
好了,完成这步后打开UCenter,你会发现:通讯成功!
UCenter测试通讯成功仅仅是调用了一个test函数,只要上述配置没写错,那就会显示通讯成功!但是它还不能实现任何功能,需要把上面的那些函数完善~

第三步是在网站中调用UCenter的接口了

这个超级简单,其实“UCenter通讯原理”那部分代码
为什么要Respon.Write这段东西?
这和UCenter同步登陆原理有关,向UCenter传递信息,告诉它要同步登陆后,它不会自己通知别的子站,而是返回一段JS,需要你的网站调用这段JS,然后通知各个子站
OK了~所有配置完成~

我对原来的那个DLL做了什么?

1、原来的DLL编码部分有几个严重的问题,导致编码错误,无法提交表单
2、原来的DLL估计是很久以前写的,里面序列化和反序列化的时候,规则和现在UCenter的规则不同,我根据现在的规则,修改了一下
3、修复别的一些小BUG

类库下载&示例代码

代码的原始作者找不到了,遵循开源精神
另外不保证目前代码全部正确,我只是测试了几个函数
类库源代码:下载
示例代码:下载
原文来自于:http://www.cnblogs.com/dozer/archive/2010/09/21/ucenter-api_with_asp-net.html,作者:Dozer

在其他系统注册用户后,如何到discuz的时候自动激活

其实任何一个系统都一样,在A系统注册后,如何到B系统自动激活?其实很方便,修改一下每一个系统的uc.php中的synlogin方法即可,我这里以discuz 7为例写了一个简单的(我没有判断uid无效的情况,实际应用中需注意,但其实问题也不大,因为返回的值被你自己插入后也全是空,如果为了严谨,还是加上一个判断较好)

function synlogin($get, $post) {
$uid = $get['uid'];
$username = $get['username'];
if(!API_SYNLOGIN) {
return API_RETURN_FORBIDDEN;
}

require_once $this->appdir.’./forumdata/cache/cache_settings.php’;

$cookietime = 2592000;
$discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);
header(‘P3P: CP=”CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR”‘);
$uid = intval($uid);
$query = $this->db->query(“SELECT username, uid, password, secques FROM “.$this->tablepre.”members WHERE uid=’$uid’”);
if($member = $this->db->fetch_array($query)) {
_setcookie(‘sid’, ”, -86400 * 365);
_setcookie(‘cookietime’, $cookietime, 31536000);
_setcookie(‘auth’, _authcode(“$member[password]\t$member[secques]\t$member[uid]“, ‘ENCODE’, $discuz_auth_key), $cookietime);
} else {
require_once $this->appdir.’./uc_client/client.php’;

list($tmp['uid'], $tmp['username'], $tmp['email']) = uc_get_user($uid,1);
$password = md5(time());
$onlineip = $_SERVER['REMOTE_ADDR'];
$timestamp = time();
$secques = ”;
$this->db->query(“INSERT INTO “.$this->tablepre.”members (uid, username, password, secques, adminid, groupid, regip, regdate, lastvisit, lastactivity, posts, credits, email, showemail, timeoffset, pmsound, invisible, newsletter) VALUES (‘$uid’, ‘$username’, ‘$password’, ”, ’0′, ’10′, ‘$onlineip’, ‘$timestamp’, ‘$timestamp’, ‘$timestamp’, ’0′, ’0′, ‘”.$tmp['email'].”‘, ’0′, ’9999′, ’1′, ’0′, ’1′)”);
$this->db->query(“REPLACE INTO “.$this->tablepre.”memberfields (uid) VALUES (‘$uid’)”);
_setcookie(‘sid’, ”, -86400 * 365);
_setcookie(‘cookietime’, $cookietime, 31536000);
_setcookie(‘auth’, _authcode(“$password\t$secques\t$uid”, ‘ENCODE’, $discuz_auth_key), $cookietime);
}
}

这其中有几个需要注意一下,新用户在discuz中涉及到两个表:member和memberfield。然后member的表字段,请尽量与我上面写的一致,否则有可能还需要让你手工激活。(至于为什么,我忘了,这是一年前的代码了。)

Ucenter API通讯失败常见可能

一般来说正常情况下,在ucenter管理中心可以对于应用管理可以查看通讯是否成功。其实很正常,遇到这种问题大多有以下几种原因:

1、uc_client目录下有一个cache目录,是否可写?通讯成功后应该会把所有的应用的信息全部存入一个cache文件的。如果不可写,可能会出错【这个情况应该是在调用updateapps方法的时候才会出现】

2、检查一下,是否有那种define定义的变量没有设置而在uc.php中存在,如果直接access dined的话,也会通讯失败,因为返回的结果不是1。

3、检查一下程序代码是否错误,这也是通讯可能失败的原因,比如方法不存在,但是方法的调用是在if判断中,正常情况下,if判断可能用不到这个函数,因此检查起来就比较繁琐。

以上是我的一点小看法,也被人误会过。http://www.discuz.net/redirect.php?goto=findpost&ptid=1606517&pid=13611356,在这里,就被人误会成了我是在骗点击。唉。真可悲

配置全正确还是通信失败原来是BOM的问题

修改好ucenter后台后再去修改论坛下的config.inc.php(其他应用也一样),不要使用记事本打开修改,txt保存utf8文件时会自动给文件添加 BOM,这时后台就会显示通信失败,建议使用UltraEdit编辑,保存时选择另存 “utf8 – 无BOM”

–EOF–

一般情况下,记事本在编辑和保存UTF8文件时,会自动加上BOM,但BOM虽然在文件中不显示,但在网页传输时,有3个字节的输出,因此可能会导致uc获取到的值与预想的不一致。所以,必须去掉BOM才可以。

小知识:

BOM

在UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输 字符”ZERO WIDTH NO-BREAK SPACE”。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM 来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。

UTF-8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可 以看到开头的FFFE了。这是个标识UTF-8编码文件的好办法,软件通过BOM来识别这个文件是否是UTF-8编码,很多软件还要求读入的文件必须带 BOM。可是,还是有很多软件不能识别BOM。我在研究Firefox的时候就知道,在Firefox早期的版本里,扩展是不能有BOM的,不过 Firefox 1.5以后的版本已经开始支持BOM了。现在又发现,PHP也不支持BOM。

参考:http://www.w3.org/International/questions/qa-utf8-bom

关于uc通信状态调试为1,但通信显示不成功

本文来自discuz官方论坛,由用户fangshawn发表。

最近帮网友调试uc通信不成功;经调试后发现,通信其实是成功的,但由于服务器无法确定返回的字符长度,所以采用Chunked编码动态的提供body内 容的长度;通信把编码应该出掉的部分没去除;具体调试如下;
在之前调试采用了别人的调试方法:也介绍过给网友们;就是下面这文章;
http://www.discuz.net/viewthread … &from=favorites
我的介绍就是在这基础上吧!
echo “\$url = $url <br />\n \$status = $status<br />\n”;die(‘haha’);
在上面打印status时;status返回是 110;

我尝试着找到低层代码,返回了以下结果;
HTTP/1.1 200 OK Connection: close Date: Thu, 28 Jan 2010 05:12:41 GMT Content-Type: text ml Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-Powered-By: PHP/5.2.9-2 Transfer-Encoding: chunked 1 1 0
也就是说 Connection 是OK;但代码接口返回结果时却不是返回我们想要的 1;而返回了110;浏览器直接调试接口时,返回的又正确是1;
最后查资料发现这个小秘密 Transfer-Encoding: chunked 1 1 0 前面的1是chunked编码后的长度;最后的0是chunked的结束符;

我尝试着在接口入口处就直接返加字母a ;结果status 变成1a0了;哈哈;

这样子在uc平台后面的通信显示是不成功的,但其实是通信是通了;会不会对同步登录造成影响我未有深入,(因为最后网友神奇的说重起了服务器就通信成功 了)
个人觉得不影响,因为同步登录是是有标签的,Chunked编码并不会改变标签;
哈哈;大家觉的呢

如果大家发因为上面的chunked编码导致同步登录不正常,留言;我会试着找出解决的方法
麻烦不找我,我也不去找麻烦;哈哈

我是曾经遇到,通信正常,但其他操作不正常,那是因为无法往应用的uc目录下写文件,还有uc_client的Cache目录。都必须要可写。因为uc的配置文件和uc_client中的Cache都会在更改应用信息的时候同步更新。

第三方整合到ucenter同步登录基本要点

这是一篇来自官方论坛的文章,大家可以在学习的同时,看看是否有什么可以借鉴的。

第三方,以下简称ot;ucenter简称uc
一、在ucenter里注册ot实用,调试通信成功(细节不表);
二、实现注册同步
1.找到ot的注册函数;用uc_get_user()验证是否用户已在注册;
(1)未注册:继续本地验证,继续第2点;
(2)已注册:检测本地用户是否注册;
[1] 已注册:提示该用户名已注册过,不允许注册;
[2] 未注册:提示该用户已注册,但未本地激活;弹出激活页面;
2.继续本地用户验证(假设成功),把用户未加密的密码用新变量保存(注册ucenter需要原始密码以保证ucenter的其它应用使用);
3.先执行ucenter注册uc_user_register;检测返回结果是否成功;
4.ucenter注册成功;获得uc_id;
5.修改本地用户注表,增加一个uc_id字段以保存注册成功返回的uc_id,实现用户的信息的关联(当然也可以用原有用户表id跟uc_id关 联,discuz就是这样处理的本地用户id)
6.把用户信息添加到本地数据库;记得保存uc_id;
三、实现ot登录,同步其他应用也登录
1.找到ot登录代码块,先采用uc_user_login登录密码验证,
2.验证成功用获得的uid跟本地的会员表id关联,查出相应信息,再发送相应本地用户cookie;
3.执行uc_user_synlogin($id);执行同步登录
四、实现共他应用登录,ot也同步登录;
1.找到ot正常登录后所要执行的cookie登录操作代码;
2.找到ot的接口:uc.php;找到synlogin 代码块,参照上一点的代码修改原接口康盛的cookie操作代码;
五、实现其他应用登出,ot也同步登出
登出跟登录的修改要点刚好相反,不细表;把原在uc.php的synlogin 注册的cookie操作执行注销就OK
六、实现ot退出,其他实用也退出;
找到ot的退出代码块,执行 uc_user_synlogout();
七、用户信息修改;
找到ot的用户修改资料代码块,缓存用户的密码(未经过加密的);在允行执行修改时先执行uc_user_edit;再执行本地用户资料修乞讨

这是我之前作的一次整合后总结;经验不足,请大家多交流发表意见

作者:fangshawn,来自http://www.discuz.net/thread-1564551-1-1.html

[疑问解答]自己的程序和uc整合遇到的怪事

Discuz论坛用户tianfq提到:

同步登入登出都没有问题,程序都是PHP的,现在遇到个怪现象
list($uid,$username,$password) = uc_user_login(sqlencode(trim($_POST["username"])),sqlencode(trim($_POST["password"])));
获取到uc的用户信息
然后下面就遇到怪事了,在uc_user_login这个方法的下面,所有的mysql_query都不能正确执行,哪位大大能帮忙解决下

膘叔解答:

一般情况下,如果出现这种情况,大致原因可能是:

1、您采用了MYSQL连接了UC

2、自已没有采用DB类,而习惯性的采用了mysql_connect等默认函数

错误原因:

当你采用mysql方式连接UC后,执行UCenter的函数时,会调用uc_client中内置的DB类,即调用了它的mysql_connect方法,在调用后相当于切换到了一个新的connectionId,而你在没有使用DB类操作时,往往直接就mysql_query($sql),而忽略了这个函数的第二个参数$conn,即你原始的db连接。

例:

$conn = mysql_connect(“…”) //这是你自己的$conn

list($uid,$username,$password) = uc_user_login(sqlencode(trim($_POST["username"])),sqlencode(trim($_POST["password"]))); //这个时候已经切换到了uc的Db连接

mysql_query($sql);//如果不带$conn参数,会使用最近的一个数据库连接,相当于,你连接到了uc数据库了。。所以操作会失败

改正方法为:mysql_query($sql,$conn);//即,继续使用你原来的数据库连接。。

写的有点乱

[来自官方论坛]Ucenter与wordpress整合

纯转载,来源地址为:http://www.discuz.net/thread-1359599-1-3.html,由于文章内部转载的地止已经无法打开,故仅留官方论坛的地址了

1.首先并在ucenter中添加应用,其中通信密钥随便输入,复制下来。
2.在wordpress目录中放一个uc_client文件夹,这个文件夹可以从ucenter相关的包中得到,是所有客户应用都会用到的。
3.更改wordpress配置文件wp-config.php,在其中加类似如下代码(请自行修改加粗部分):
//added by afo…
//登录和退出时发出通知
define(’UC_CONNECT’, ‘mysql’); // 连接 UCenter 的方式
define(’UC_DBHOST’, ‘localhost’); // UCenter 数据库主机
define(’UC_DBUSER’, ‘wpdb_f’); // UCenter 数据库用户名
define(’UC_DBPW’, ‘1fdsf2_=’); // UCenter 数据库密码
define(’UC_DBNAME’, ‘wpdb’); // UCenter 数据库名称
define(’UC_DBCHARSET’, ‘utf8′); // UCenter 数据库字符集
define(’UC_DBTABLEPRE’, ‘wpdb.uc_’); // UCenter 数据表前缀
define(’UC_DBCONNECT’, ‘1′); // UCenter 数据库持久连接
define(’UC_KEY’, ‘fksal43dfssdfwerfdssafsafsafsafdsaf’); // 与 UCenter 的通信密钥
define(’UC_API’, ‘http://blog.treeber.com/ucenter’); // UCenter URL
define(’UC_CHARSET’, ‘utf-8′); // UCenter 的字符集
define(’UC_IP’, ”); // UCenter 的 IP
define(’UC_APPID’, 3); // 当前应用的 ID
define(’S_ROOT’, substr(dirname(__FILE__), 0));
define(’UC_CLIENT_ROOT’, S_ROOT.’/uc_client/’);
include_once(UC_CLIENT_ROOT.’client.php’);
将下面的api文件解成api目录后放在wordpress目录下,然后调试下确保从ucenter中可以与wordpress正常通信。
api for wordpress
4.需在index.php中第一行,即
define(’WP_USE_THEMES’, true);
前添
ini_set(’output_buffering’, ‘On’);
5.找到这个文件:
wp-includes/pluggable.php中,搜function logout(),约468行,在}即函数结束前添如下代码
//added by afo…
//加uc登出通知
uc_dsetcookie(’auth’, ”);
echo uc_user_synlogout();
exit();
在其下(即}之后,endif;之前)添加一个函数,代码如下
function uc_dsetcookie($var, $value, $life = 0, $prefix = 1) {
global $cookiedomain, $cookiepath, $_SERVER;
setcookie($var, $value, $life ? time() + $life : 0, $cookiepath,$cookiedomain, $_SERVER['SERVER_PORT'] == 443 ? 1 : 0);
}
6.找到这个文件:
user.php文件中
搜function wp_signon,在下面的return前(约59行)加
//added by afo…
echo uc_user_synlogin($user->ID);
Author: kolidon

当心uc_feed_get函数的第二参数

discuz的几乎所有程序都有一个:加入事件,那么,其他平台怎么获取这个事件呢?那就是uc_feed_get方法了。但 事实上,有很多人都没有注意过这个方法有第二个参数。

如果你直接去uc的uc_feed表查看,你会经常性的发现这个表是空的。而autoIndex却很大,这又是为什么呢?

于是打开uc_client方法,却发现这个函数有两个参数,第一个参数是一次获取的条数,第二个参数是Delete,默认值为true,也就是说,获取完多少条后,他都会自动删除。因此uc_feed表就几乎是永远空着了。

但问题也就在这里。我怎么知道是哪个系统获取了这些feed呢?A系统获取Feed后,B是不是也获取到了呢。又怎么处理的呢?没有仔细看代码。。。

文档中uc_feed_get只提供了一个参数limit,可以查看:http://www.ucapi.com/api/feed.htm

建议在开发的时候,先找一个uc_feed_get方法。自己写程序处理。否则,应该会出现a系统有Feed,B系统没有Feed的情况。【慎重,主要还是没有队列。。。或者推送机制,否则在有了feed后,同步向各个系统推送一下Feed呢?不就结束了?】

uchome的UC接口中有一个addfeed方法,理论上是应该在其他系统添加后往uc.php推送的。只是没有看到哪里有推送代码。。。难道是放在事件中?迷惘了。