免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 10691 | 回复: 9
打印 上一主题 下一主题

通行证的基本原理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-16 01:54 |只看该作者 |倒序浏览
单点登录(SSO)的实现---通行证的基本原理
附件:


问题起源:想做一个中型的网站,因为势单力薄。部分模块采用整合其它开源系统的方案,比如BBS系统和BLOG系统。首先面临的就是用户身份认证的方式。由于这些不是自己开发的系统,都分别有自己的用户系统,于是面临统一身份认证的过程。

以前看过企业级的Webservice方案,主要是通过XML,SOAP,WSDL和UDDI来实现。将应用服务都注册到UDDI服务器中,通过SOAP协议使用XML传递信息(当然需经过加密)。由于涉及到很多服务部署的问题,用JAVA来做这样的项目肯定是再好不过的了。我的目的只是几个WEB系统的整合,肯定是要排除这么伟大的方案了。关于Web service有兴趣的朋友可以参考机械工业出版社出版的《Web Servides原理与研发实践》,里面有详细的介绍。

那么对于这样的小WEB系统的整合该怎么来实现?假设这是个从零开始的项目,除了自己开发的系统外,还要用到一些其它组织开发的开源系统:比如BLOG,CMS,BBS。这些系统都有各自的用户系统。要把他们整合到一块有个原则,就是尽量不要破坏或者修改这些系统。那么要实现统一身份认证,我们必须要有一个用户信息库,然后吧这个数据库的信息映射到那些子系统的数据库中,在大型项目中,一般都会独立出一台单独的用户信息服务器,大部分高校采用LOAD来存放用户信息,因为采用的是树状结构,对经常读取但很少修改的数据而言,它的性能是很高的。

LOAD用户库和 各子系统用户库的映射有很多种方法,我这里只用最简单的直接映射,也就是帐号和密码都是相同的。

假设我的域名部署如下

http://news.domain.com 这是CMS系统的域名

http://bbs.domain.com 这是论坛的域名

http://blog.domain.com 这是博客域名

http://reg.domain.com 这是统一注册和登录页面的域名
首先是注册,我们让所有子系统注册页面都转向到一个注册页面上来(各种脚本语言都有转向函数),比如说当用户希望在http://blog.domain.com/reg.php注册是,reg.php把这个请求转向到http://reg.domain.com/reg.php.

在实现注册时,由于刚开始时候子系统并不多,注册时把用户注册信息写入主用户数据库的同时写入各子系统的用户库。以后若有新的子系统加入进来时可以通过帐号激活的方式来实现新系统的帐号激活。

用户登录的过程,可以参考如下来自IBM的图片



流程描述如下:(仅描述正常流程)

1. 用户使用在统一认证服务注册的用户名和密码(也可能是其他的授权信息,比如数字签名等)登陆统一认证服务;

2. 统一认证服务创建了一个会话,同时将与该会话关联的访问认证令牌返回给用户;

3. 用户使用这个访问认证令牌访问某个支持统一身份认证服务的应用系统;

4. 该应用系统将访问认证令牌传入统一身份认证服务,认证访问认证令牌的有效性;

5. 统一身份认证服务确认认证令牌的有效性;

6. 应用系统接收访问,并返回访问结果,如果需要提高访问效率的话,应用系统可选择返回其自身的认证令牌已使得用户之后可以使用这个私有令牌持续访问。

上面所说的令牌我在WEB引用中可以用COOKIE或者SEESION来实现。

例如通过COOKIE来实现

1.用户在统一登录页登陆,通过查询主用户数据库判断用户是否合法,若是,则注册该用户的唯一COOKIE标识(可以通过加密用户名和密码得到,网上有很多算法)。

2.用户进入某子系统时,先判断COOKIE是否注册,若注册了 ,则解密该COOKIE得到用户名和帐号,并判断合法性。

3.如果合法,则立刻在该子系统中注册(可在原子系统的登录脚本种抽出登录的部分做成一个函数)

使用COOKIE的优点就是简单,只要设置一下就可以实现COOKIE的跨子域传递

例如

setcookie(NC_USER_COOKIE, 用户名, 失效时间, 作用路径, '.domain.com');

就能实现在所有.domain.com子域下的传递。

但COOKIE也有他的缺点,首先就是安全级别不高,要提防COOKIE劫持的威胁,其次就是它只能跨子域传递,而不能跨完全不同的域。比如说domain.com和fanghei.com之间就不能传递。

然后再说下SESSION的方式,由于SESSION是存储在服务器端的,所以安全级别肯定要比COOKIE的级别高。但是由于SEESION存储的位置不同,造成了无法跨域传递 。可以通过把SEESION村入数据库来解决这个问题。

由于PHP的SESSION需要用到标识SESSION的COOKIE,所以需要设置下COOKIE的作用域

<?

ini_set(’session.cookie_domain’, ‘.domian.com’);

?>

然后重写PHP的SESSION操作函数。

PHP 提供了session_set_save_handle() 函数来自定义 SESSION 的处理过程,先将 session.save_handler 改成 user

session_module_name(‘user’);

然后几可以重写SESSION的操作了,下面是PHP牛人NIO写的SESSION操作
[php]
  1. <?php
  2. define(‘MY_SESS_TIME’, 3600);   //SESSION 生存时长
  3. //类定义
  4. class My_Sess
  5. {
  6.     function init()
  7.     {
  8.         $domain = ‘.infor96.com’;
  9.         //不使用 GET/POST 变量方式
  10.         ini_set(’session.use_trans_sid’,    0);
  11.         //设置垃圾回收最大生存时间
  12.         ini_set(’session.gc_maxlifetime’,   MY_SESS_TIME);

  13.         //使用 COOKIE 保存 SESSION ID 的方式
  14.         ini_set(’session.use_cookies’,      1);
  15.         ini_set(’session.cookie_path’,      ‘/’);
  16.         //多主机共享保存 SESSION ID 的 COOKIE
  17.         ini_set(’session.cookie_domain’,    $domain);

  18.         //将 session.save_handler 设置为 user,而不是默认的 files
  19.         session_module_name(‘user’);
  20.         //定义 SESSION 各项操作所对应的方法名:
  21.         session_set_save_handler(
  22.             array(‘My_Sess’, ‘open’),   //对应于静态方法 My_Sess::open(),下同。
  23.             array(‘My_Sess’, ‘close’),
  24.             array(‘My_Sess’, ‘read’),
  25.             array(‘My_Sess’, ‘write’),
  26.             array(‘My_Sess’, ‘destroy’),
  27.             array(‘My_Sess’, ‘gc’)
  28.         );
  29.     }   //end function

  30.     function open($save_path, $session_name) {
  31.         return true;
  32.     }   //end function

  33.     function close() {
  34.         global $MY_SESS_CONN;

  35.         if ($MY_SESS_CONN) {    //关闭数据库连接
  36.             $MY_SESS_CONN->Close();
  37.         }
  38.         return true;
  39.     }   //end function

  40.     function read($sesskey) {
  41.         global $MY_SESS_CONN;

  42.         $sql = ‘SELECT data FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey) . ‘ AND expiry>=’ . time();
  43.         $rs =& $MY_SESS_CONN->Execute($sql);
  44.         if ($rs) {
  45.             if ($rs->EOF) {
  46.                 return ‘’;
  47.             } else {    //读取到对应于 SESSION ID 的 SESSION 数据
  48.                 $v = $rs->fields[0];
  49.                 $rs->Close();
  50.                 return $v;
  51.             }   //end if
  52.         }   //end if
  53.         return ‘’;
  54.     }   //end function

  55.     function write($sesskey, $data) {
  56.         global $MY_SESS_CONN;
  57.         
  58.         $qkey = $MY_SESS_CONN->qstr($sesskey);
  59.         $expiry = time() + My_SESS_TIME;    //设置过期时间
  60.         
  61.         //写入 SESSION
  62.         $arr = array(
  63.             ’sesskey’ => $qkey,
  64.             ‘expiry’  => $expiry,
  65.             ‘data’    => $data);
  66.         $MY_SESS_CONN->Replace(’sess’, $arr, ’sesskey’, $autoQuote = true);
  67.         return true;
  68.     }   //end function

  69.     function destroy($sesskey) {
  70.         global $MY_SESS_CONN;

  71.         $sql = ‘DELETE FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey);
  72.         $rs =& $MY_SESS_CONN->Execute($sql);
  73.         return true;
  74.     }   //end function

  75.     function gc($maxlifetime = null) {
  76.         global $MY_SESS_CONN;

  77.         $sql = ‘DELETE FROM sess WHERE expiry<’ . time();
  78.         $MY_SESS_CONN->Execute($sql);
  79.         //由于经常性的对表 sess 做删除操作,容易产生碎片,
  80.         //所以在垃圾回收中对该表进行优化操作。
  81.         $sql = ‘OPTIMIZE TABLE sess’;
  82.         $MY_SESS_CONN->Execute($sql);
  83.         return true;
  84.     }   //end function
  85. }   ///:~

  86. //使用 ADOdb 作为数据库抽象层。
  87. require_once(‘adodb/adodb.inc.php’);
  88. //数据库配置项,可放入配置文件中(如:config.inc.php)。
  89. $db_type = ‘mysql’;
  90. $db_host = ‘192.168.212.1′;
  91. $db_user = ’sess_user’;
  92. $db_pass = ’sess_pass’;
  93. $db_name = ’sess_db’;
  94. //创建数据库连接,这是一个全局变量。
  95. $GLOBALS[‘MY_SESS_CONN’] =& ADONewConnection($db_type);
  96. $GLOBALS[‘MY_SESS_CONN’]->Connect( $db_host, $db_user, $db_pass, $db_name);
  97. //初始化 SESSION 设置,必须在 session_start() 之前运行!!
  98. My_Sess::init();
  99. ?>
复制代码

[/php]

基本的原理差不多就这样,细节方面就看还有很多需要琢磨的地方.

看了下家园网的通行证件实现,我猜应该是通过SESSION来实现的吧

登录页面为:http://reg.ncuhome.com/reg.asp

登录成功后转到:http://reg.ncuhome.com/Cindex.asp

然后通过向http://reg.ncuhome.com/gonewpage.asp?url=这个页面传递子系统参数来分别登陆子系统.

登陆了这么多次发现这样做存在一个问题,那就是如果如果想要登陆任何一个子系统都要重新返回http://reg.ncuhome.com/Cindex.asp来登陆.

当我通过http://reg.ncuhome.com/Cindex.asp进入了BLOG系统后虽然显示我登陆了博客,如果我在浏览器里输入http://bbs.ncuhome.com时候,论坛依然显示我没登陆.而要跑Cindex.asp去点社区进入论坛才能登陆.感觉这有点违背了单点登陆,全站通行的目标了.希望家园网能把这点完善下.

[ 本帖最后由 fan12 于 2008-9-16 01:56 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2008-09-16 01:54 |只看该作者

新浪网通行证的制作原理

通行证
网络媒体的建立是一个渐进的过程,这就决定了网络媒体各系统是由不同的模块像积木一样搭建而成。这样的积木系统发展到一定程度,必然会遇到用户数据库难以统一、共享的问题,导致BBS论坛用户无法在聊天室中发言,聊天室用户不能在Blog上写日志,Blog用户无法获得高速下载权限等等问题,总之,新发展的功能系统无法做到原有注册用户无需再注册即可使用。

这里有两个原因:第一、各个系统模块是在不同时期,由不同程序员开发的;第二、即便费尽九牛二虎之力将所有模块的用户注册系统重新写,也无法解决全部问题:一、从各系统模块吸引而来的注册,要求的注册信息各不相同,为统一求全,一次要求用户注册非常多的信息,不现实;二、各系统模块的管理员只有权限管理本系统,而统一注册势必造成非常多的超级管理员能看到整个网络媒体的所有注册用户信息,这不符合公司的用户信息保密要求。

通行证不是简单的统一用户认证,它是通过将用户认证与用户权限剥离,使得网络媒体各系统既可以独立发展,又能共享既有用户。

它的逻辑是,通行证只负责用户的身份认证,用户的权限还在各模块。用户不管从哪个模块登录都要到通行证服务器认证,通过对账号和密码的认证,通行证服务器负责告诉各模块,他是不是“他”,如果是他,各模块再赋予他权限。

举例来说,BBS用户A要发帖子,通行证服务器立即提醒他要登录,他输入账号和密码,通行证服务器根据账号和密码辨别他是不是A,如果不是,返回“密码错误”,如果是,通行证服务器就立即通知BBS模块,这是A,BBS模块此时立即将A接管过来,并调阅用户数据库,赋予A在论坛里的权限(是普通用户、还是VIP用户、还是斑竹、他在论坛里的积分是多少,等等)。

这样设计的优点在于:由于是统一登录,所以,一有新的服务,通行证就会在用户登录的第一时间告知用户,如果是免费服务,可以为所有用户开启这个模块的权限,让他们无需再注册,即可使用;如果是付费服务,则可以不断地提醒注册用户付费开启这一服务。

有了通行证,网络媒体再也无需多虑用户注册问题,各系统模块可以独立自由地去开发建设;有了通行证,网络媒体就将所有的积木系统都串了起来。

通行证不仅可以使本站新开启的服务无需注册即可使用,如果两个网站使用统一的通行证,那么这两个网站的用户也可以共享。
谢谢

论坛徽章:
0
3 [报告]
发表于 2008-09-16 02:01 |只看该作者

分析pw的通行证原理

实现程序主要有:require/passport_server.php,passport_client.php

每次登录或注册时,服务端都会包含require/passport_server.php

来看看passport_server.php里面有什么东西

  1. <?php
  2. !function_exists('readover') && exit('Forbidden');

  3. if(!$passport_ifopen || $passport_type != 'server'){
  4.   Showmsg('passport_close');
  5. }

  6. !$forward && $forward = $db_cmsurl;
  7. $clienturl=explode("\n",str_replace("\r","",$passport_urls));

  8. //题外话,这段代码我看不出有什么特别,只觉得好像一无用处
  9. //因为passort_urls就是在设置服务器端的<通行证客户端地址>如果我有好几个,
  10. //他也只是取第一个不是空的就可以了。这样的话,完全是没有作用,
  11. //还不如直接定义一个客户端地址
  12. $jumpurl='';
  13. while(!$jumpurl){
  14.   $jumpurl=array_shift($clienturl);
  15. }
  16. if(!$jumpurl){
  17.   Showmsg('undefined_action');
  18. }

  19. $userdb = array();
  20. foreach($clienturl as $key=>$val){
  21.   if($val && $val != $jumpurl){
  22.     $userdb['url'] .= $userdb['url'] ? ",$val" : $val;
  23.   }
  24. }

  25. $rt=$db->get_one("SELECT uid,username,password,email,rvrc,money,credit FROM pw_user WHERE uid='$winduid'");

  26. //这里就是接口所用到的其中一个重要数据
  27. //有占类似支付宝的接口
  28. //但显然是后其更改进的
  29. //看下面的注释
  30. $userdb['uid']    = $rt['uid'];
  31. $userdb['username']  = $rt['username'];
  32. $userdb['password']  = $rt['password'];
  33. $userdb['email']  = $rt['email'];
  34. $userdb['rvrc']    = $rt['rvrc'];
  35. $userdb['money']  = $rt['money'];
  36. $userdb['credit']  = $rt['credit'];
  37. $userdb['time']    = $timestamp;
  38. $userdb['cktime']  = $cktime;

  39. //看这里是将数据转成字符串
  40. $userdb_encode='';
  41. foreach($userdb as $key=>$val){
  42.   $userdb_encode .= $userdb_encode ? "&$key=$val" : "$key=$val";
  43. }
  44. //这里就是跟支付宝不同的地方,支付宝在这里是公开的。而这里使用了StrCode加密。
  45. //相对来说是比支付宝要安全很多。但也可能出现错误,因为strcode可能会出显“=”号
  46. //现在直接用str_replace换掉“=”号,这种做法不可取。因为这样做为程序造成一个不稳定因素
  47. //这应该是lyn经常会说pw灵义事件之一了
  48. //其实这个str_replace 是完全没有必要的。因为下面使用了rawurlencode

  49. $db_hash=$passport_key;
  50. $userdb_encode=str_replace('=','',StrCode($userdb_encode));

  51. if($action=='login'){

  52.   //verify,就是跟支付宝差不多的东西。确保数据没有变更改过。
  53.   $verify = md5("login$userdb_encode$forward$passport_key");

  54.   //主要提供三个变量到客户端去就可以完成服务端的工作了。

  55. ObHeader("$jumpurl/passport_client.php?action=login&userdb=".rawurlencode($userdb_encode)."&forward
  56. =".rawurlencode($forward)."&verify=".rawurlencode($verify));
  57. }elseif($action=='quit'){
  58.   $verify = md5("quit$userdb_encode$forward$passport_key");

  59. ObHeader("$jumpurl/passport_client.php?action=quit&userdb=".rawurlencode($userdb_encode)."&forward
  60. =".rawurlencode($forward)."&verify=".rawurlencode($verify));
  61. }
  62. ?>
复制代码


当客户端接收到服务器提交的三个变量后

  1. <?php
  2. require_once('global.php');
  3. require_once(R_P.'mod/checkpass_mod.php');

  4. if(!$passport_ifopen || $passport_type != 'client'){
  5. exit("Passport closed(VeryCMS)");
  6. }

  7. if(md5($action.$userdb.$forward.$passport_key) != $verify){
  8. exit('Illegal request(VeryCMS)');
  9. }
  10. $_db_hash=$db_hash;

  11. //还原用户数据
  12. $db_hash=$passport_key;
  13. parse_str(StrCode($userdb,'DECODE'),$userdb);

  14. if($action=='login'){
  15. foreach($userdb as $key=>$val){
  16.    $userdb[$key] = addslashes($val);
  17. }
  18. if(!$userdb['time'] || !$userdb['username'] || !$userdb['password'] || !$userdb['email']){
  19.    exit("Lack of parameters(VeryCMS)");
  20. }
  21. if($timestamp-$userdb['time']>3600){
  22.    exit('Passport request expired(VeryCMS)');
  23. }

  24. //题外话:这里里verycms的一段特殊接口,不知道为什么会另外写一般关于verycms的接口,而不是使用一个统一的接口,
  25. //如果以后还其它的程序是不是还要另外加一段程序?
  26. //verycms passport group right
  27. $groupright=1;
  28. include_once(D_P.'data/cache/passport.php');
  29. if($ps_combine){
  30.    $db->select_db($ps_bbsdbname);
  31.    $PW = $ps_bbspre;

  32.    $gp = $db->get_one("SELECT hk_value FROM pw_hack WHERE hk_name='bg_groups'");
  33.    $rt = $db->get_one("SELECT groupid,memberid FROM pw_members WHERE uid='$userdb[uid]'");
  34.    Add_S($rt);
  35.    Add_S($gp);

  36.    $groupid = $rt['groupid'] == '-1' ? $rt['memberid'] : $rt['groupid'];
  37.    if($gp['hk_value'] && strpos($gp['hk_value'],",$groupid,")===false){
  38.      $groupright=0;
  39.      $forward .= "/?nogroupright";
  40.    }
  41.    include (D_P.'data/sql_config.php');
  42.    $db->select_db($dbname);
  43. }
  44. //verycms passport group right

  45. if($groupright){
  46.    $user_field = array('username','password','email');
  47.    $credit_field = array('rvrc','money','credit');

  48.    $sql='';
  49.    foreach($user_field as $key=>$val){
  50.      $sql .= ','.$val;
  51.    }
  52.    foreach($credit_field as $key=>$val){
  53.      $sql .= ','.$val;
  54.    }
  55.    //先看查是否有这个用户,使用的是用户名作关联,这个方案是不错,毕竟不是所有论坛程序都会有uid,
  56.    //pw的另一套程序ofstar
  57.    $rt=$db->get_one("SELECT uid $sql FROM pw_user WHERE username='$userdb[username]'");
  58.    if($rt){
  59.      $sql='';
  60.      foreach($userdb as $key=>$val){
  61.        if($rt[$key] != $val){
  62.          if(in_array($key,$user_field)){
  63.            $sql  .= $sql ? ",$key='$val'" : "$key='$val'";
  64.          }elseif(in_array($key,$credit_field) && strpos(",$passport_credit,",",$key,")!==false){
  65.            $sql .= $sql ? ",$key='$val'" : "$key='$val'";
  66.          }
  67.        }
  68.      }
  69.      if($sql){
  70.        $db->update("UPDATE pw_user SET $sql WHERE uid='$rt[uid]'");
  71.        $db->update("UPDATE pw_domain SET bbsuid='$userdb[uid]',username='$userdb[username]' WHERE uid='$rt[uid]'");
  72.      }
  73.      $winduid = $rt['uid'];
  74.    }else{
  75.      $sql1=$sql2='';
  76.      foreach($userdb as $key=>$val){
  77.        if(in_array($key,$user_field)){
  78.          $sql1 .= $sql1 ? ','.$key  : $key;
  79.          $sql2 .= $sql2 ? ",'$val'" : "'$val'";
  80.        }elseif(in_array($key,$credit_field) && strpos(",$passport_credit,",",$key,")!==false){
  81.          $sql1 .= $sql1 ? ','.$key  : $key;
  82.          $sql2 .= $sql2 ? ",'$val'" : "'$val'";
  83.        }
  84.      }
  85.      //刚开始我在想,论坛的用户是怎么在blog里直接建一个帐呢?原来就是用Replace关键字来建的。
  86.      //下面三个$db->update都是会对数据作出更改或添加。可是我很奇怪。
  87.      //为什么在register.php文件里会对以下的数据进行过滤,而在这里却一点都不过滤就直接使用呢?
  88.      //是不是很相信,使用pwblog程序的人只会使用pwfourm呢?
  89.      //但就算都是用pwfourm,那用户使用中文呢?
  90.      //有点不负责任的感觉。
  91.      $db->update("REPLACE INTO pw_user($sql1,groupid,memberid,gender,regdate,signchange) VALUES($sql2,'-1','8','0','$timestamp','1')");
  92.      $winduid = $db->insert_id();
  93.      //
  94.      $db->update("REPLACE INTO pw_domain(uid,bbsuid,username,blogname) VALUES ('$winduid','$userdb[uid]','$userdb[username]','$userdb[username]')");

  95.      $db->update("UPDATE pw_bloginfo SET newmember='$userdb[username]',totalmember=totalmember+1 WHERE id='1'");
  96.    }

  97.    $db_hash=$_db_hash;
  98.    $windpwd = confuse($userdb['password']);

  99.    Cookie("bloguser",StrCode($winduid."\t".$windpwd),$userdb['cktime']);
  100.    Cookie('lastvisit','',0);
  101.    Loginipwrite();
  102.    if($userdb['url']){
  103.      $clienturl = explode(',',$userdb['url']);
  104.      $jumpurl='';
  105.      while(!$jumpurl){
  106.        $jumpurl=array_shift($clienturl);
  107.      }
  108.      $userdb['url'] = implode(',',$clienturl);
  109.    }
  110. }

  111. if($jumpurl){
  112.    $userdb_encode='';
  113.    foreach($userdb as $key=>$val){
  114.      $userdb_encode .= $userdb_encode ? "&$key=$val" : "$key=$val";
  115.    }
  116.    $db_hash=$passport_key;
  117.    $userdb_encode=str_replace('=','',StrCode($userdb_encode));

  118.    $verify = md5("login$userdb_encode$forward$passport_key");
  119.    ObHeader("$jumpurl/passport_client.php?action=login&userdb=".rawurlencode($userdb_encode)."&forward
  120. =".rawurlencode($forward)."&verify=".rawurlencode($verify));
  121. }else{
  122.    ObHeader($forward ? $forward : $passport_serverurl);
  123. }
  124. }elseif($action=='quit'){
  125. $db_hash=$_db_hash;
  126. Loginout();

  127. if($userdb['url']){
  128.    $clienturl = explode(',',$userdb['url']);
  129.    $jumpurl='';
  130.    while(!$jumpurl){
  131.      $jumpurl=array_shift($clienturl);
  132.    }
  133.    $userdb['url'] = implode(',',$clienturl);
  134. }

  135. if($jumpurl){
  136.    $userdb_encode='';
  137.    foreach($userdb as $key=>$val){
  138.      $userdb_encode .= $userdb_encode ? "&$key=$val" : "$key=$val";
  139.    }
  140.    $db_hash=$passport_key;
  141.    $userdb_encode=str_replace('=','',StrCode($userdb_encode));

  142.    $verify = md5("quit$userdb_encode$forward$passport_key");
  143.    ObHeader("$jumpurl/passport_client.php?action=quit&userdb=".rawurlencode($userdb_encode)."&forward=
  144. ".rawurlencode($forward)."&verify=".rawurlencode($verify));
  145. }else{
  146.    ObHeader($forward ? $forward : $passport_serverurl);
  147. }
  148. }

  149. function Loginipwrite($winduid){
  150. global $db,$timestamp,$onlineip;

  151. $logininfo="$onlineip|$timestamp|6";
  152. $db->update("UPDATE pw_user SET lastvisit=thisvisit,thisvisit='$timestamp',onlineip='$logininfo' WHERE uid='$winduid'");
  153. }
  154. ?>
复制代码

[ 本帖最后由 fan12 于 2008-9-16 02:03 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2008-09-16 02:16 |只看该作者

discuz Passport 通行证 整合笔记

记住,网上找到自己需要的资料也要耗时间的!!!!!!

Passport 通行证 整合第一篇:整合原理
请注意: 整合不成功可能造成的后果-----dz论坛无法登录,无法管理
解决办法:

第一步: 到dz的数据库表cdb_settings 找到下面这几行修改为
setting.gif (4.3 KB)
2006-9-30 13:59


第二步: 删除dz安装目录/forumdata/cache/cache_settings.php
第三步: 重新访问论坛



登陆注册整合流程
用户从登陆或注册表单提交帐号密码信息 ==>
主站程序检验用户登陆或注册,成功(注册需要生成新用户)则 ==>
设置主站自身的cookie或session    ==>
url传递 返回地址forward和编码后的用户信息和其他信息 到dz/api/passport.php

整合之前请先仔细阅读官方passport技术文档: http://www.discuz.net/usersguide/advanced_passport.htm
代码:<?php
//该文档保存为login.php
//首先将接口技术文档里的加密解密函数拷贝
//为了不让代码太乱,我拷贝到文档的结尾处
//假设自己的用户数据库表里用户名字段为UserName, 密码字段为Pwd, Email字段为 Email
//注册页实现方法差不多,可自行实现,疑问加我QQ:2666556
$act=(isset($_GET['act']))?$_GET['act']:"login";
if(function_exists($act)) $act();else login();        
function login()
{        
        $ErrMsg=UserCheck();
        if($ErrMsg!="")echo $ErrMsg;        
        //后面加上显示你的登陆表单的代码 如
?>

<form action=login.php?act=login method=post>
用户名:<input name=username>
密码:<input name=password>
<input name=submit type=submit value=登陆></form>

<?php
}//end function
function logout()//登出
{
        $passportkey="1234567890";//这里换成你论坛通行证设置的passportkey
        $auth=$_COOKIE['auth'];
        setcookie("auth", "",time() - 3600);
        $forward=$_GET['forward'];
        if($forward=="")$forward="../../index.php";//这里换成你的主页绝对地址或相对地址                                       
        $verify = md5('logout'.$auth.$forward.$passportkey);
        $auth=rawurlencode($auth);
        $forward=rawurlencode($forward);
        header("Location: bbs/api/passport.php?action=logout&auth=$auth&forward=$forward&verify=$verify");
}
function UserCheck()
{        
        $passportkey="1234567890";//这里换成你论坛通行证设置的passportkey
        //===========验证输入=====================
        if(!isset($_POST['submit'])) return; // login表单的按钮需要与此同名
        $usnm=$_POST['username'];//username换成你登陆表单里的用户名域                        
        $pwd=$_POST['password'];//password换成你登陆表单里的密码域                        
        if($usnm=="") return "请输入用户名!";
        if($pwd=="") return "请输入密码!";
        //=========数据库处理==========================
        $db=mysql_connect("localhost", "root", "");
        mysql_select_db("your_db_name");
$sql="Select * from `user` where UserName='".$usnm."' Limit 1";        
        $rs = mysql_query($sql,$db)        ;
        $row = mysql_fetch_array($rs);
        if(!$row)return "该用户不存在";
        if($row["Pwd"]!=md5($pwd))return "密码错误";
        mysql_free_result($rs);        
        //==============header到bbs=====================        
        $member = array
        (
                        'time'     => time(),
                        'username' => $row["UserName"],
                        'password' => $row["Pwd"],
                        'email'    => $row["Email"]
        );
        $auth = passport_encrypt(passport_encode($member), $passportkey);
        setcookie("auth",$auth,($_POST["Cookie"]? time()+(int)$_POST["Cookie"] :0));
        $forward=$_POST['forward'];
        if($forward=="")$forward="../../index.php";                                       
        $verify = md5('login'.$auth.$forward.$passportkey);
        $auth=rawurlencode($auth);
        $forward=rawurlencode($forward);
header("Location:bbs/api/passport.php?action=login&auth=$auth&forward=$forward&verify=$verify");
}

//=============================================================
//=============以下为拷贝过来的函数============================
function passport_encrypt($txt, $key) {
        srand((double)microtime() * 1000000);
        $encrypt_key = md5(rand(0, 32000));        
        $ctr = 0;
        $tmp = '';
        for($i = 0; $i < strlen($txt); $i++) {
                        $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
                        $tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
        }
        return base64_encode(passport_key($tmp, $key));
}
function passport_decrypt($txt, $key) {
        $txt = passport_key(base64_decode($txt), $key);
        $tmp = '';
        for ($i = 0; $i < strlen($txt); $i++) {
                 $tmp .= $txt[$i] ^ $txt[++$i];
        }
        return $tmp;
}
function passport_key($txt, $encrypt_key) {
        $encrypt_key = md5($encrypt_key);
        $ctr = 0;
        $tmp = '';
        for($i = 0; $i < strlen($txt); $i++) {
                        $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
                        $tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
        }
        return $tmp;
}
function passport_encode($array) {
        $arrayenc = array();
        foreach($array as $key => $val) {
                   $arrayenc[] = $key.'='.urlencode($val);
        }
        return implode('&', $arrayenc);
}
//=========================================================================
//===========================拷贝结束======================================
?>


[ 本帖最后由 fan12 于 2008-9-16 02:21 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2008-09-16 02:25 |只看该作者

详解ucenter原理及第三方应用程序整合思路、方法

整合了 ucenter就等于整合了整个php界的所有php程序。

一、功用:
不对,何止整合了整个php界的所有php程序,而是整合了 ucenter就等于整合了所有的asp/asp.net/php/jsp等其它语言的所有web应用程序。(因为ucenter client的api开发包,dz会推出其它语言版)

而且所有整合的程序同步登录,同步退出,同步修改密码。最终用户可以通过它轻松通行在各个应用之中,无需重复登录、注册、退出

一个id可以出入一个站内的所有程序,如cms和bbs,也可以一个id出入www.a.com和www.b.com及www.c.com

a.com的用户可以和b.com的用户互发短消息pm,a.com的用户可以和b.com的用户加好友。

而且可以实现站内信(pm)和其它任意站的任意程序的站内信(pm)互通。

可以实现www.a.comwww.b.com及[url=http://www.c.com]www.c.com[/url]共享一个用户库,www.a.com的用户可以给www.b.com的用户pm短信.

用户组与discuz不对应的问题也得到解决。因为一般应用程序的用户组是单独的一个应用,和discuz再没有关系,而是和ucenter有关系,而ucenter没有用户组的概念。


二、整合方法:
如果单说整合用户的话,整合时应用程序的改动也非常小,原数据库不用动,原写cookies的代码不用动,原写数据库session的代码不用动,原来的程序不用怎么动,只需改动以下4个文件:

longin.php  register.php  logout.php  修改密码文件.php  (忘记密码.php不用动,用原来的就可以)

4个文件中加上和ucenter api通信的语句和逻辑结构。

另需要针对ucenter新增加一个文件uc.php,这个文件就是应用程序接收ucenter传来的指令并执行的文件。而且他利用p3p技术实现反向登录a.com或是同时反向登录a.com/b.com/c.com的dedecms或是phpcms或是任意所有程序。


共计改4个文件,增一个文件。


如果要是整合站内信pm,好友,头像等功能,思路与方法和整合用户类同。

另外有一点,在不同的系统之间注册的用户,在第一次登录这个从来没有登录过的系统时,会让激活。借用这个激活,可以让用户完善在本应用程序中的资料,如企业注册资料。资料不完善,不可以激活。

如在bbs注册一个用户test,第一次来到b2b的程序中,这时,并不会自动登录,而是要求用户激活,而test在bbs注册时填的字段与b2b中要求的不同。这时让用户完善资料。第二次来就会自动登录。


三、 uc原理:

以用户登录为例介绍,其它注销,改密码,消息,头像,好友均类同。

1.
从用户xxx在某一应用程序的login.php,输入用户名,密码讲起。
先用uc_user_login函数到uc server验证此用户和密码,如正确,则写入session,写入cookies,并更新应用程序会员表中的登录ip,登录时间。用户感觉不到这个过程。
2.
然后通过uc_user_synlogin通知uc server 用户xxx登录成功,这个过程可能使用ajax,用户感觉不到通知过程。
3.
uc server收到这个消息后,马上命令手下,把xxx登录的消息,像令牌环一样,发给所有愿意接收(后台中那个是否开启同步登录)这个消息的其它应用程序。其实就是带参数访问一下各应用程序的uc.php,用户感觉不到这个过程。
4.
各应用程序靠api下的uc.php来接收uc server发来的消息,并对uc server言听计从,让干什么就干什么。现在,收到让xxx用户在你的程序中登录的命令,马上执行。
并写本应用程序的session,并且使用p3p, 写入相同域或不同域的cookies.  用户感觉不到这个过程。

5.最后所有和uc整合的程序,xxx均登录成功。用户从www.test.com/bbs登录后, 跳到www.test.com/news同样显示登录。因为bbs 和news系统在后台均已登录。

6.应用程序与uc server的会话结束。


得益于uc设计的精巧过程,整个过程,用户完全感觉不到ucenter的存在.这是整合程序历史上的创新。完

论坛徽章:
0
6 [报告]
发表于 2008-09-16 10:28 |只看该作者
感谢。慢慢看

论坛徽章:
0
7 [报告]
发表于 2008-09-16 10:40 |只看该作者
总结的不错。

论坛徽章:
0
8 [报告]
发表于 2009-07-30 11:02 |只看该作者
我正在找这方面的东西,谢谢楼主

论坛徽章:
0
9 [报告]
发表于 2009-07-31 14:54 |只看该作者

回复 #8 zhdooo 的帖子

不客气,也是扒别人的

论坛徽章:
0
10 [报告]
发表于 2009-07-31 14:56 |只看该作者
如果不是转载,而是讲详细一点你就能得到小手了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP