解读UCenter 源码并整合自建应用

最近在略微研究了下UCenter,原因是想利用其做一个小应用。这里做些简单研究,并转来IT驿站的一些内容,算作笔记。

如何整合UCenter自建应用

UCenter 就是一个“用户中心”,它不仅可以整合康盛集团中其他产品,而且可以整合自己开发的一些应用,它给我们提供了很多的接口函数,只要把自己的应用和UCenter服务器建立通信,在应用上使用UCenter提供的接口,就可以把我们的用户统一用UCenter管理起来了。

下面介绍一下整合的具体步骤,以整合UCenter自带的项目为例。

首先在服务器端,打开“应用管理”,点击“添加新应用”,在出现的“选择安装方式”,我们选择“自定义安装”,这个时候就会出现一个配置表单,里面有很多需要填写的值,而且在每个需要填写值的后面都有文字说明,这里不一一赘述,只把必填项和注意事项给大家列出来一下:

  • 应用名称
  • 应用的 URL
  • 通信密钥:这里需要填写一个和你客户端应用相同的密钥
  • 应用类型:这里选择“其他”,因为我们的应用不属于康盛集团的任一产品
  • 应用接口文件名称:不用变,按默认值即可

就只需要这么多的必填项,点击“提交”以后,在这个页面的下边会生成一个文本框,复制这个文本框的内容,然后在你的自建应用的配置文件中粘贴这些代码,他们都是应用和UCenter服务器端通信的配置信息,大体解释一下这些常量的含义:

UC_CONNECT
连接 UCenter 的方式
mysql:MySQL 方式
空:远程方式
UC_DBHOST
UCenter 数据库主机
UC_DBUSER
UCenter 数据库用户名
UC_DBPW
UCenter 数据库密码
UC_DBNAME
UCenter 数据库名称
UC_DBCHARSET
UCenter 数据库字符集
UC_DBTABLEPRE
UCenter 数据库表前缀
UC_DBCONNECT
UCenter 数据库持久连接 0=关闭, 1=打开
UC_KEY
与 UCenter 的通信密钥, 要与 UCenter 保持一致
UC_API
UCenter 服务端的 URL 地址
UC_IP
UCenter 的 IP, 当 UC_CONNECT 为非 mysql 方式时, 并且当前应用服务器解析域名有问题时, 请设置此值
UC_CHARSET
UCenter 的字符集
UC_APPID
当前应用的 ID

配置完成以后就,你就可以返回UCenter服务器端查看“应用管理”,看看是否通信成功:

如果成功的话,恭喜你,你现在可以使用UCenter接口来测试一下用户的注册登录等功能是否完好;如果通信失败,原因有很多,以下会详细介绍诊断通信失败的方法,以及通信使用了那些函数和文件。

解读UCenter源码

UCenter 的中文意思就是“用户中心”,是 Comsenz 旗下各个产品之间信息直接传递的一个桥梁,通过 UCenter 站长可以无缝整合 Comsenz 系列产品,甚至其它更多的第三方应用程序,实现用户的一站式登录,个人信息、积分等的统一管理。

想了解Comsenz 旗下的各个产品,UCenter算是一个基础。源码的下载地址是:UCenter1.5.0下载

1、UCenter 的目录结构

UCenter 分为服务端和客户端 2 个部分。在下载的压缩包里可以发现里面有四个文件夹。

其中“advanced/”目录包括:接口开发手册,一个实例,还有客户端代码在“uc_client/”目录;“readme/”目录是一些授权协议和安装升级文档;“upload/”目录里是服务器端代码;“utilities/”目录包括几个使用工具的PHP文件。在压缩包里最重要的就是服务端目录为“upload/”,客户端目录为“client/”。无论你的网站有多少个应用程序,服务端在一个网站中只需安装一次。而客户端则 必须存在于每一个应用的根目录中。具体目录结构请见下表:

服务端:upload/

api/
应用程序于UCenter接口部分目录
control/
应用程序 control 部分目录
data/
缓存数据目录,此目录及其子目录需要有可写权限
images/
图片目录
install/
安装目录,安装后必须删除
js/
javascript 脚本目录
lib/
应用程序函数库目录
model/
应用程序 model部分目录
plugin/
应用程序插件部分目录
upgrade/
应用程序升级部分目录
view/
模板目录
index.php
服务端接口函数库接口文件
admin.php
用户管理中心主文件
avatar.php
头像显示

客户端:uc_client/

control/
应用程序 control 部分目录
data/
缓存数据目录,此目录及其子目录需要有可写权限
lib/
应用程序函数库目录
module/
应用程序 module 部分目录
client.php
接口函数库

 

2、服务器接口函数库接口文件

看完以上,应该知道服务端接口的函数库接口文件是Ucenter根目录下的index.php,打开这个文件,咋一看是比较简单的,一共一百多行的代码,却要处理所有来自于客户端的请求,包括客户端的登录注册,好友,短消息等等。下面我们来看一下这个文件究竟都做了些什么工作。

具体的代码就不拷贝到这里了,相信对PHP有些基础的人都能看懂,简单的我就大体做个总结,如果有一些重要的难懂的代码我会摘出来一些,具体讲解。

这个文件刚开始是定义了一些常量:比如说Ucenter的版本,还有根目录接口地址等,下面把这些常量的名字列出,大家可以统一的查看一下:UC_SERVER_VERSION,UC_SERVER_RELEASE, IN_UC, UC_ROOT, UC_API, UC_DATADIR, UC_DATAURL, MAGIC_QUOTES_GPC。

还设置了一些运行的环境 ,像错误的显示和魔术引号的设置:
error_reporting(0); set_magic_quotes_runtime(0);

然后给所有的环境变量做转义,就是在获取变量的时候加反斜杠,函数名为daddslashes(),这个函数是在此文件最下面定义的,接下来就是包含Ucenter服务器端的配置文件 config.inc.php,到此准备工作已经准备好了,开始获取接口文件的参数了。

从接口文件获取参数一共是三个:m,a,release。其中m表示的是模型(Module),a表示的是动作(Action)。获取参数的函数是getgpc(),这个函数也是在此文件最下面定义的,默认是根据$_REQUEST来获取。可能有些朋友还不清楚接口文件的参数是如何发起,参数都会传递什么内容,这里也给大家一个例子:请求是客户端在执行任何一个操作都会发起的,调用接口文件时传递的参数可以参考这种形式:

m=user&a=synlogin&inajax=2&release=20090121&input=623dF06edspDaA18gn9ktFaXFN7nx9WTmWJUOBd%2Fm25BKuz9tqbOEtTTywsgKP2JgLFEni%2BP0Gjx60S8AQL6nwzBV1jyoU09iVlikFgR9RAwDfUHTTxPlso&appid=3

这样大家对请求就会有一个感官上的认识了,具体这些参数都有什么作用,我会在后面详细介绍。继续回到index文件,获取这三个参数以后会做一个判断,如果m和a这两个参数同时没有的话,就会跳转到用户管理中心主文件(admin页面)。如果有这两个参数的话,会继续设置RELEASE_ROOT路径,然后包含UC的基础类base.php。再下面会判断参数m的值是否在允许的模型(model)范围之内,如果在的话就包含 control目录下的此模型文件,也就是m的值是control目录下文件的名字,参数a的值是此文件里的一个方法,如果同时存在的话,就调用此函数,如果不是就给一些错误的提示。上面的这个接口地址的意思就是:包含 control/user.php,然后调用其中的 onsynlogin() 方法。

这个文件的主要工作就是这么多了,在文件的最下面有两个函数:daddslashes() 和getgpc(),这两个函数的作用前面已经用到,一个是给环境变量的值加反斜杠的,一个是从环境变量中获取值的。

 

3、用户管理中心主文件

UCenter的另外一个非 常重要的文件:用户管理中心主文件,它位于Ucenter根目录下的admin.php。打开这个文件,我们可以发现里面的代码和务端接口函数库接口文件 的代码差不多。只有几个很小的地方不一样,也就是包含的类的文件不一样。这里只把里面不一样的代码解释一下。

首先,刚进入这个页面的时候,依然是定义了一下常量和环境变量,然后再 包含”model/base.php”之后,要比接口文件多包含一个admin的文件”require UC_ROOT.’model/admin.php’;”,这个文件是admin的一个基类,之后所有控制应用的类都是继承了这个类,而且在这个类里定义 了sid的加密和解密规则。阅读了上一篇接口文件的文章后,在传接口参数的时候有一个值是sid,也许有人还不了解sid是如何生成,并且包含哪些信息, 这里也顺便给大家提示一下,sid就是将$_SERVER[‘HTTP_USER_AGENT’],UC_KEY,IP还有用户名进行复杂编码后的产物。

接 下来,同样是从REQUEST获取m,a。其中m表示的是模型(Module),a表示的是动作(Action)。然后判断参数m的值是否在允许的控制应 用范围之内,如果在的话就包含 control目录下的此控制文件,也就是m的值是control目录下文件的名字,参数a的值是此文件里的一个方法,如果同时存在的话,就调用此函数, 如果不是就给一些错误的提示。

下面我们来解释一下,当我们还没有登陆的时候,为什么在访问ucenter根目录的时候会直接跳转到登陆页 面:首先大家知道,当我们直接访问根目录的时候会首先调用index.php,而在这个文件里有做一个判断,如果没有任何参数的时候,会直接跳转到 admin.php文件,然后在这个文件里也会判断有没有参数$_REQUEST[‘m’],$_REQUEST[‘a’],如果没有的话就会给这两个参 数设置 默认值:$m=frame,$a=index,然后再去调用/control/admin/frame.php里的onindex()方法。这个时候就有 发现了,其实在onindex()方法里,只是显示Ucenter的用户管理首页,那是在什么地方判断登陆状态呢?这也是有一些可能忽略的问题,我在这里 强调一下,每一个类都有自己的构造函数,构造函数很简单,就是在构造这个对象的时候会自动调用的一个函数,构造函数可以有两种表示方 法:__construct(), 还有一个就是和类名相同的一个方法名,这个是JAVA一样的,不过JAVA支持重载,也就是一个函数可以有很多个构造函数,但是在PHP里面只允许有一 个,在Ucenter里的每一个类都有构造函数,如果在构造函数没有什么特殊处理的话,就会调用父级的构造函数 parent::__construct(); 这样我们很快就可以追溯到判断登陆的那个构造函数,在/model/admin.php里adminbase()对用户登陆状态进行了判断。如果没有登陆 的话就会跳转到 admin.php?m=user&a=login&iframe= 这样的一个页面。

 

4、解决UCenter通信失败的问题

对于Uenter的初学者来说,经常会遇到的问题就是在添加新应用的时候,出现“通信失败”,查来查去也找不到具体的原因。其实导致“通信失败”的原因特别多,比如说Key值不匹配,或者配置接口地址有误。还有一些可能是服务端与客户端时间不匹配等等好多原因,这里将介绍通信机制的原理,找出具体的问题所在!

对于已出现“通信失败”问题的用户,建议你还是先阅读前面“UCenter如何整合自建应用”部分,重新配置一次,或者检查一下应用配置,如果问题依然存在,那么可以按照下面的步骤来查找问题。

首先我们应该清楚这个“通信失败”是从什么地方得来的,在应用管理页面,我们可以通过Firebug来做到这一步,如果您没有安装Firebug,可以参考这个URL:

  1. http://localhost/discuz/ucenter/admin.php?m=app&a=ping&inajax=1&url=http%3A%2F%2Flocalhost%2Fexamples&ip=&appid=3&random=19859&sid=6a6cr%2BqjOqTa8khveXdPH9SmPwU2T0ICgg2S%2FBwrBZApLl26vJCRkH2o8l7d%2FexAckUYz4bhLORnEw

也许有人对这个地址的含义不是很清楚,我大体解释一下:因为这是在admin管理后台的功能,所以代码是在/ucenter/control/admin/下,参数m=app表示是在/ucenter/control/admin/下的app.php,参数a=ping表示在在这个文件的的onping()函数,参数url就是请求的客户端的地址,参数ip指的是你在配置信息里的填写的ip,参数appid是这个应用的ID,sid指的是admin的ID,你可以此地址查看相关的代码:/ucenter/control/admin/app.php

  1. function onping() {
  2. $ip = getgpc(‘ip’);
  3. $url = getgpc(‘url’);
  4. $appid = intval(getgpc(‘appid’));
  5. $app = $_ENV[‘app’]->get_app_by_appid($appid);
  6. $status = ”;
  7. if($app[‘extra’][‘apppath’] && @include $app[‘extra’][‘apppath’].’./api/’.$app[‘apifilename’]) {
  8. $uc_note = new uc_note();
  9. $status = $uc_note->test($note[‘getdata’], $note[‘postdata’]);
  10. } else {
  11. $this->load(‘note’);
  12. $url = $_ENV[‘note’]->get_url_code(‘test’, ”, $appid);
  13. $status = $_ENV[‘app’]->test_api($url, $ip);
  14. }
  15. if($status == ‘1’) {
  16. echo ‘document.getElementById(‘status_’.$appid.”).innerHTML = “<img src=’images/correct.gif’ border=’0′ class=’statimg’ /><span class=’green’>’.$this->lang[‘app_connent_ok’].'</span>”;testlink();’;
  17. } else {
  18. echo ‘document.getElementById(‘status_’.$appid.”).innerHTML = “<img src=’images/error.gif’ border=’0′ class=’statimg’ /><span class=’red’>’.$this->lang[‘app_connent_false’].'</span>”;testlink();’;
  19. }
  20. }

这里有一个函数:如果$status为1能信才成功,其他的都是失败,在这里我们可以把变量$url和$status打印出来,$url地址就是客户端的接口地址。搞清楚这一点就好办了,我们可以直接在地址栏访问这个地址,如果他返回1,说明就通信成功了!我这里的测试地址是:

  1. http://localhost/examples/api/uc.php?code=a632n0LfAIbjl5qJHiMu7C4txDVOnCbZeBgQeQUJLub8Q1w7LAWP0Zb9tI%2FOTekdMuXHprfAHag

如果返回错误提示,我们可以按照提示来寻找错误,如果什么都没有返回,可能是文件里缺少一些变量,这里的例子(UCenter自带例子)里就有一个失误,在包含数据库类文件的时候忘记定义了$database这个变量,应该是$database=‘mysql’,这个很不起眼的失误给很多开发者造成了疑惑,所以大家在查看代码时也一定要小心,不要放过任何一点可疑之处。

  1. require_once DISCUZ_ROOT.’./include/db_’.$database.’.class.php’;

范围缩小了,我们可以打开这个文件看看它都负责做什么,首先从URL里获取code,然后给它解密,获取真实的参数,这些参数分别是来判断超时和请求类型的,然后根据请求类型连接数据库,怎么样?经过这么一圈分析,是不是感觉很容易就能找到错误的原因!


5条评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注