php序列化serialize() 与反序列化unserialize()

 更新时间:2016年11月25日 15:39  点击:2005
本文章以php中serialize() 和 unserialize()函数来引出我们在正常开发应用中对于序列化使用方法与使用序列化时的一些安全问题,希望对各位会带来帮助

把复杂的数据类型压缩到一个字符串中
 
serialize() 把变量和它们的值编码成文本形式
 
unserialize() 恢复原先变量
 
eg:

 代码如下 复制代码

$stooges = array('Moe','Larry','Curly');
$new = serialize($stooges);
print_r($new);
echo "<br />";
print_r(unserialize($new));

结果:
a:3:{i:0;s:3:"Moe";i:1;s:5:"Larry";i:2;s:5:"Curly";}
Array ( [0] => Moe [1] => Larry [2] => Curly )

当把这些序列化的数据放在URL中在页面之间会传递时,需要对这些数据调用urlencode(),以确保在其中的URL元字符进行处理:
 

 代码如下 复制代码

$shopping = array('Poppy seed bagel' => 2,'Plain Bagel' =>1,'Lox' =>4);

echo '<a href="next.php?cart='.urlencode(serialize($shopping)).'">next</a>';

margic_quotes_gpc和magic_quotes_runtime配置项的设置会影响传递到unserialize()中的数据。
 
如果magic_quotes_gpc项是启用的,那么在URL、POST变量以及cookies中传递的数据在反序列化之前必须用stripslashes()进行处理:
 

 代码如下 复制代码

 

$new_cart = unserialize(stripslashes($cart));

//如果magic_quotes_gpc开启
$new_cart = unserialize($cart);

如果magic_quotes_runtime是启用的,那么在向文件中写入序列化的数据之前必须用addslashes()进行处理,而在读取它们之前则必须用stripslashes()进行处理:
 

 代码如下 复制代码

$fp = fopen('/tmp/cart','w');

fputs($fp,addslashes(serialize($a)));

fclose($fp);

//如果magic_quotes_runtime开启
$new_cat = unserialize(stripslashes(file_get_contents('/tmp/cart')));

//如果magic_quotes_runtime关闭
$new_cat = unserialize(file_get_contents('/tmp/cart'));

在启用了magic_quotes_runtime的情况下,从数据库中读取序列化的数据也必须经过stripslashes()的处理,保存到数据库中的序列化数据必须要经过addslashes()的处理,以便能够适当地存储。

 代码如下 复制代码

mysql_query("insert into cart(id,data) values(1,'".addslashes(serialize($cart))."')");

$rs = mysql_query('select data from cart where id=1');

$ob = mysql_fetch_object($rs);

//如果magic_quotes_runtime开启
$new_cart = unserialize(stripslashes($ob->data));

//如果magic_quotes_runtime关闭
$new_cart = unserialize($ob->data);

当对一个对象进行反序列化操作时,PHP会自动地调用其__wakeUp()方法。这样就使得对象能够重新建立起序列化时未能保留的各种状态。例如:数据库连接等。

用例子给你说明一下

 代码如下 复制代码

<?php

//声明一个类

class dog {

var $name;

var $age;

var $owner;

function dog($in_name="unnamed",$in_age="0",$in_owner="unknown") {

$this->name = $in_name;

$this->age = $in_age;

$this->owner = $in_owner;

}

function getage() {

return ($this->age * 365);

}

function getowner() {

return ($this->owner);

}

function getname() {

return ($this->name);

}

}

//实例化这个类

$ourfirstdog = new dog("Rover",12,"Lisa and Graham");

//用serialize函数将这个实例转化为一个序列化的字符串

$dogdisc = serialize($ourfirstdog);

print $dogdisc; //$ourfirstdog 已经序列化为字符串 O:3:"dog":3:{s:4:"name";s:5:"Rover";s:3:"age";i:12;s:5:"owner";s:15:"Lisa and Graham";}

/*

-----------------------------------------------------------------------------------------

在这里你可以将字符串 $dogdisc 存储到任何地方如 session,cookie,数据库,php文件

-----------------------------------------------------------------------------------------

*/

//我们在此注销这个类

unset($ourfirstdog);

?>

b.php

<?php

 

?>

<?php

//声明一个类

class dog {

var $name;

var $age;

var $owner;

function dog($in_name="unnamed",$in_age="0",$in_owner="unknown") {

$this->name = $in_name;

$this->age = $in_age;

$this->owner = $in_owner;

}

function getage() {

return ($this->age * 365);

}

function getowner() {

return ($this->owner);

}

function getname() {

return ($this->name);

}

}

/*还原操作   */

/*

-----------------------------------------------------------------------------------------

在这里将字符串 $dogdisc 从你存储的地方读出来如 session,cookie,数据库,php文件

-----------------------------------------------------------------------------------------

*/

$dogdisc='O:3:"dog":3:{s:4:"name";s:5:"Rover";s:3:"age";i:12;s:5:"owner";s:15:"Lisa and Graham";}';

//我们在这里用 unserialize() 还原已经序列化的对象

$pet = unserialize($dogdisc); //此时的 $pet 已经是前面的 $ourfirstdog 对象了

//获得年龄和名字属性

$old = $pet->getage();

$name = $pet->getname();

//这个类此时无需实例化可以继续使用,而且属性和值都是保持在序列化之前的状态

print "Our first dog is called $name and is $old days old<br>";

?>

序列化与反序列化语法解析不一致带来的安全隐患

. PHP string serialize() 相关源码分析
------------------------------------

 代码如下 复制代码

static inline void php_var_serialize_string(smart_str *buf, char *str, int len) /* {{{ */
  {
smart_str_appendl(buf, "s:", 2);
smart_str_append_long(buf, len);
smart_str_appendl(buf, ":\"", 2);
smart_str_appendl(buf, str, len);
smart_str_appendl(buf, "\";", 2);
  }

 


通过上面的代码片段可以看到 serialize() 对 string 序列化处理方式如下:

 

 代码如下 复制代码
$str = 'ryatsyne';
  var_dump(serialize($str));
  // $str serialized string output
  // s:8:"ryatsyne";

ii. PHP string unserialize() 相关源码分析
---------------------------------------

unserialize() 函数对 string 的反序列化则分为两种,一种是对 `s:` 格式的序列化 string 进行处理:

 代码如下 复制代码

switch (yych) {
  ...
case 's':  goto yy9;
...
  yy9:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy46;
goto yy3;
...
  yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
if (yych <= '/') goto yy18;
if (yych <= '9') goto yy48;
goto yy18;
  yy47:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
if (yych >= ':') goto yy18;
  yy48:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
if (yych <= '/') goto yy18;
if (yych <= '9') goto yy48;
if (yych >= ';') goto yy18;
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
{
size_t len, maxlen;
char *str;
 
len = parse_uiv(start + 2);
maxlen = max - YYCURSOR;
if (maxlen < len) {
  *p = start + 2;
  return 0;
}
 
str = (char*)YYCURSOR;
 
YYCURSOR += len;
 
if (*(YYCURSOR) != '"') {
  *p = YYCURSOR;
  return 0;
}
  // 确保格式为 s:x:"x"
 
YYCURSOR += 2;
*p = YYCURSOR;
  // 注意这里,*p 指针直接后移了两位,也就是说没有判断 " 后面是否为 ;
 
INIT_PZVAL(*rval);
ZVAL_STRINGL(*rval, str, len, 1);
return 1;

 

另一种是对 S: 格式的序列 string 进行处理(此格式在 serialize() 函数序列化处理中并没有定义):

 代码如下 复制代码

static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen)
  {
size_t i, j;
char *str = safe_emalloc(*len, 1, 1);
unsigned char *end = *(unsigned char **)p+maxlen;
 
if (end < *p) {
  efree(str);
  return NULL;
}
 
for (i = 0; i < *len; i++) {
  if (*p >= end) {
efree(str);
return NULL;
  }
  if (**p != '\\') {
str[i] = (char)**p;
  } else {
unsigned char ch = 0;
 
for (j = 0; j < 2; j++) {
  (*p)++;
  if (**p >= '0' && **p <= '9') {
ch = (ch << 4) + (**p -'0');
  } else if (**p >= 'a' && **p <= 'f') {
ch = (ch << 4) + (**p -'a'+10);
  } else if (**p >= 'A' && **p <= 'F') {
ch = (ch << 4) + (**p -'A'+10);
  } else {
efree(str);
return NULL;
  }
}
str[i] = (char)ch;
  }
  (*p)++;
}
str[i] = 0;
*len = i;
return str;
  }
  // 上面的函数是对 \72\79\61\74\73\79\6e\65 这样十六进制形式字符串进行转换
  ...
  switch (yych) {
...
case 'S':  goto yy10;
// 处理过程与 s: 相同
if ((str = unserialize_str(&YYCURSOR, &len, maxlen)) == NULL) {
  return 0;
}
// 处理过程与 s: 相同

 

从上面的代码片段可以看到 unserialize() 对序列化后的 string 反序列化处理如下:

 代码如下 复制代码

$str1 = 's:8:"ryatsyne";';
  $str2 = 's:8:"ryatsyne"t';
  $str3 = 'S:8:"\72\79\61\74\73\79\6e\65"';
  var_dump(unserialize($str));
  // $str1, $str2 and $str3 unserialized string output
  // ryatsyne;

 

iii. 语法解析处理不一致导致的安全隐患
-----------------------------

从上述分析过程可以看到 PHP 在反序列化 string 时没有严格按照序列化格式 s:x:"x"; 进行处理,没有对 " 后面的是否存在 ; 进行判断,同时增加了对十六进制形式字符串的处理,这样前后处理的不一致让人很费解,同时由于 PHP 手册中对此没有详细的说明,大部分程序员对此处理过程并不了解,这可能导致其在编码过程中出现疏漏,甚至导致严重的安全问题。

回到文章开头提到的 IPB 漏洞上,利用这个 funny feature of PHP 可以很容易的 bypass safeUnserialize() 函数的过滤:)

 代码如下 复制代码

* mixed safe_unserialize(string $serialized)
* Safely unserialize, that is only unserialize string, numbers and arrays, not objects
*
* @license Public Domain
* @author dcz (at) phpbb-seo (dot) com
*/
static public function safeUnserialize( $serialized )
{
// unserialize will return false for object declared with small cap o
// as well as if there is any ws between O and :
if ( is_string( $serialized ) && strpos( $serialized, "\0" ) === false )
{
if ( strpos( $serialized, 'O:' ) === false )
{
// the easy case, nothing to worry about
// let unserialize do the job
return @unserialize( $serialized );
}
else if ( ! preg_match('/(^|;|{|})O:[+\-0-9]+:"/', $serialized ) )
{
// in case we did have a string with O: in it,
// but it was not a true serialized object
return @unserialize( $serialized );
}
}

return false;
}
 
// a:1:{s:8:"ryatsyne"tO:8:"ryatsyne":0:{}}
// 只要构造类似的序列化字符串就可以轻易突破这里的过滤了

在php中利用curl伪造来源IP是非常的方法的,下面来给大家介绍一个php 伪造来源IP的例子,但经过测试不能伪造$_SERVER["REMOTE_ADDR"]的来源。

test.php文件

 代码如下 复制代码

<?php
ob_start();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.xxx.cn/test/test2.php");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:1.1.1.1', 'CLIENT-IP:2.2.2.2'));  //伪造IP
curl_setopt($ch, CURLOPT_REFERER, "http://www.111cn.net/ ");   //伪造来源网址
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_exec($ch);
curl_close($ch);

$out = ob_get_contents();
ob_clean();
echo $out;

?>

test2.php文件代码如下

 代码如下 复制代码

<?php
function getClientIp() {
if (!empty($_SERVER["HTTP_CLIENT_IP"]))
$ip = $_SERVER["HTTP_CLIENT_IP"];
else if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
else if (!empty($_SERVER["REMOTE_ADDR"]))
$ip = $_SERVER["REMOTE_ADDR"];
else
$ip = "err";
return $ip;
}

echo "<br />IP: " . getClientIp() . "  HTTP_CLIENT_IP-: " . $_SERVER["HTTP_CLIENT_IP"] . "    HTTP_X_FORWARDED_FOR-: " . $_SERVER["HTTP_X_FORWARDED_FOR"] . "    REMOTE_ADDR-: " . $_SERVER["REMOTE_ADDR"] . "   ";
echo "<br />referer: " . $_SERVER["HTTP_REFERER"];
?>

执行结果:

HTTP/1.1 200 OK
Server: DWS/01.03Z33
Date: Mon, 09 Jun 2014 09:27:09 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding

<br />IP: 2.2.2.2  HTTP_CLIENT_IP-: 2.2.2.2    HTTP_X_FORWARDED_FOR-: 1.1.1.1   
REMOTE_ADDR-: 127.0.0.1   <br />referer: http://www.111cn.net/

但是暂时还无法伪造骗过:

$_SERVER["REMOTE_ADDR"]。

所以建议大家记录IP时使用$_SERVER["REMOTE_ADDR"]。

在php中如果我们统一编码是没有什么问题了,但是很多朋友会发现一个问题就是utf8和gbk编码中返回的值会有所区别了,下面一聚教程小编就来介绍它们的一些问题。

php 在utf8和gbk编码下使用serialize和unserialize互相序列化和反序列化会出现无法成功反序列化的问题。
问题出现的原因主要是在不同编码下strlen函数计算中文字符串长度不同的原因。

 代码如下 复制代码
<?php
$array=array('title'=>'php教程分享网','url'=>'http://www.111cn.net');
echo serialize($array);
//gbk编码  a:2:{s:5:"title";s:13:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";}
//utf8编码 a:2:{s:5:"title";s:18:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";}
?>

要解决这个问题就要在反序列化的时候重新修正字符串的长度。
解决方案

 代码如下 复制代码

<?php

$str='a:2:{s:5:"title";s:13:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";}';
$regex = '/s\:(\d+)\:\"([^\"]+)\"/isx';

$str = preg_replace_callback(

$regex ,

"fixser",

$str);

function fixser($matches)

{

 return 's:'.strlen($matches[2]).':'.'"'.$matches[2].'"';

}

?>

 
可以改成匿名函数

 代码如下 复制代码

<?php
$str='a:2:{s:5:"title";s:13:"php教程分享网";s:3:"url";s:20:"http://www.111cn.net";}';
$regex = '/s\:(\d+)\:\"([^\"]+)\"/isx';

$str = preg_replace_callback(
$regex ,
function ($matches)
{
 return 's:'.strlen($matches[2]).':'.'"'.$matches[2].'"';
},
$str);

?>

全局变量是面向对象程序员遇到的引发bug的主要原因之一。这是因为全局变量将类捆绑于特定的环境。破坏了封装。如果新的应用程序无法保证一开始就定义了相同的环境变量,那么一个依赖于全局变量的类就无法从一个应用程序中提取出来并应用到新的应用程序中。

什么是单例模式呢

单例模式顾名思义,就是只有一个实例。

作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,

这个类我们称之为单例类。

单例模式的要点有三个:

它们必须拥有一个构造函数,并且必须被标记为private
它们拥有一个保存类的实例的静态成员变量
它们拥有一个访问这个实例的公共的静态方法

和普通类不同的是,单例类不能在其他类中直接实例化。单例类只能被其自身实例化。要获得这样的一种结果, __construct()方法必须被标记为private。如果试图用private构造函数构造一个类,就会得到一个可访问性级别的错误。
要让单例类起作用,就必须使其为其他类提供一个实例,用它调用各种方法。单例类不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。结果是单例类不会重复占用内存和系统资源,从而让应用程序的其它部分更好地使用这些资源。作为这一模式的一部分,必须创建一个空的私有__clone()方法,以防止对象被复制或克隆。

返回实例引用的这个方法通常被命名为getInstance()。这个方法必须是静态的,而且如果它还没有实例化,就必须进行实例化。getInstance() 方法通过使用 instanceof 操作符和self 关键字,可以检测到类是否已经被实例化。

 代码如下 复制代码

header("Content-type:text/html;charset=utf-8");
//单例测试类
class Test {
    private $unique;
    static private $instance;//静态属性保存该类实例
     
    private function __construct(){//构造方法私有(防止外界调用)
        $this->unique=rand(0,20000);
    }
    static public function getInstance(){//静态方法提供对外接口(获取实例)
        if(!self::$instance instanceof self){
            self::$instance=new self();
        }
        return self::$instance;
    }
    private function __clone(){}//私有克隆方法,防止外界直接克隆该实例
    
}
$test=Test::getInstance();
$test2=Test::getInstance();
    
print_r($test);
print_r($test2);
    
if($test===$test2){
    echo '相等!';
}else{
    echo '不相等!';
}

好了,该说书代码了,我们在程序中查询数据的操作会非常非常的多,我们不可能每次都new一个对象,这样太耗费开销了。那么我们怎么办呢,单例模式是个不错的选择。 单例模式:只能实例化一次

下面看一下代码

db.class.php

 

 代码如下 复制代码

<?php
//数据库类、单例模式
class db{
public $conn;
private  static $sql;
private static $instact = null;

private function __construct(){
require_once('db.config.php');
$this->conn = mysql_connect($db['host'],$db['user'],$db['psd']);
if(!mysql_select_db($db['databases'],$this->conn)){
echo "数据库连接错误";
}
mysql_query('set names utf8',$this->conn);
}

public static function getInstance(){
if(is_null(self::$instact)){
self::$instact = new  db;
}
return self::$instact;
}
/**
*数据查询
*/
public function select($table,$condition=array(),$field=array()){
$where ="";
if(!empty($condition)){
foreach($condition as $k=>$v){
$where.= $k."='".$v."' and ";
}
$where = "where ".$where." 1=1";
}
if(!empty($field)){
foreach($field as $k=>$v){
$fieldstr.= $v.",";
}
$fieldstr = rtrim($fieldstr);
}else{
$fieldstr = "*";
}
self::$sql = "select {$fieldstr} from {$table} {$where}";
$result =  mysql_query(self::$sql);
$i = 0;
while($row = mysql_fetch_assoc($result)){
foreach($row as $k=>$v){
$resultrow[$i][$k] = $v;
}
$i++;
}
var_dump($resultrow);
}

/**
*数据添加
*/
public function insert($table,$data){
$values = "";
$datas = "";
foreach ($data as $k=>$v){
$values.=$k.",";
$datas.="'$v'".",";
}
$values = rtrim($values,',');
$datas = rtrim($datas,',');
self::$sql = "insert into {$table}({$values}) values ({$datas})";
if(mysql_query(self::$sql)){
return mysql_insert_id();
}else{
return false;
};
}

/**
*数据更新
*/
public function update($table,$data,$condition=array()){
$where = "";
if(!empty($condition)){
foreach ($condition as $k=>$v){
$where.= $k."=".$v." and ";
}
$where="where ".$where."1=1";
}
$updatastr = "";
if(!empty($data)){
foreach($data as $k=>$v){
$updatastr.= $k."='".$v."',";
}
$updatastr= "set ".rtrim($updatastr,",");
}
self::$sql = "update {$table} {$updatastr} {$where}";
return mysql_query(self::$sql);
}
/**
*数据 删除
*/
public function delete($table,$condition){
$where = "";
if(!empty($condition)){
foreach( $condition as $k=>$v){
$where.=$k."='".$v."' and ";
}
$where = "where ".$where.'1=1';
}
self::$sql = "delete from {$table}  {$where}";
return mysql_query(self::$sql);
}
//打印sql
public function getlastsql(){
echo self::$sql;
}
}
$ne = db::getInstance();
//$ne->update('message',array('user'=>'wanghao','title'=>'sd'),array('id'=>'5'));
//echo $db->insert('message',array('user'=>'张三','title'=>'demo'));
$ne->select('message',array('user'=>'songlin'));
$ne->getlastsql();
?>

数据库的配置文件

db.config.php


<?php
$host = "localhost";  //主机地址
$user = "root";         //用户名
$psd= "";                 //密码
$databases = "ceshi";

$db = array( "host" =>$host,
"user" =>$user,
"psd" =>$psd,
"databases"=>$databases
);
?>

在以前如果我们要实现php+ajax实现多域名跨域登录的话很多朋友都碰到无法正常跨域登录问题,下面我来给大家解决跨域名登录问题,有需要的参考。

该同步登陆需求需满足以下三个关键点:
1)A域名下登陆的用户,跳转到B域名下时实现同步登陆;
2)B域名下点击A域名下的退出链接,实现A域名与B域名的同时退出
3)用户直接访问B域名时,可以自动判断A域名下是否存在用户登录,如果存在,则实现B域名下的用户同步登陆。
首先解决跨域的同步登陆登出,有以下可行的解决思路:
1)两个域共享SESSION服务器,即统一的内存服务器,这样两个域下的SESSIONID会相同,会自动无缝实现同步登陆登出;
但该解决方案需要额外的硬件投入,并且需要懂这方面部署的技术人员
2)通过url传递sessionid
3)通过P3P协议获取跨域的SESSION
为解决该需求,我经过查询各种资料,并拟定了自己的一套解决方案,分享给大家,其要点如下:
1)当用户在A域名下登录后,访问A域名下网站时,会ajax请求B域下的js脚本文件,写入B域COOKIE及SESSION,实现B域下登录;
2)当用户在A域名下退出后,访问A域名下网站时,会ajax请求B域下的js脚本文件(同登录参数不同),清除B域COOKIE及SESSION,实现B域下登出;
3)当用户直接访问B域网页时,请求A域下脚本,判断A域下是否存在登录,如果存在,则将COOKIE及SESSION赋值到当前域的网页中,通过ajax实现当前域
COOKIE及SESSION的写入。
我将a域名设定为A域名;B域名设定为B域名。
以下是相关代码:

 代码如下 复制代码

#############a域名ApiController.php #################
 /*
  * 根据当前域(a域名)的cookie信息设置bstv域下的cookie及session
  * 如果为空,则清空处理
  * */
 function setckAction() {
  $clr = $this->_request->getParam("clr");
  $bts_user = ($clr) ? '' : urlencode($_COOKIE['bts_LOGGED_USER']);
  $this->view->assign("bts_user",$bts_user);
 }
 
 /*
  * 提供给bstv域下,获取当前域(a域名)的cookie及session
  * 以脚本变量返回给bstv浏览器端
  * */
 function getckAction() {
  header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
  echo 'var ck="'.$_COOKIE['bts_LOGGED_USER'].'";';
  echo 'var sess="'.$_SESSION['user']['uid'].'";';
  exit();
 }
#############a域名index.php #################
##### 默认指定index.php为登录返回的页面
<script src="http://www.B域名/api/setck?bts_user={$bts_user}"></script>
############# B域名 api.php #################
 
 /*
  * 显示a域名域下的cookie及SESSION
  * 赋值到js变量
  * js 通过ajax写入cookie和session:如果a域名已退出,则bstv做退出处理
  * */
 function getbtsck() {
  include $this->template->getfile('api/getbtsck');
 }
 
 /*
  * a域名域下设置bstv域下的cookie及session
  * 如果为空,则清空处理
  * */
 function setck() {
  header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
 
  $bts_user = trim($_GET['bts_user']);
  if(!empty($bts_user)) {
   setcookie("bts_LOGGED_USER", urldecode($bts_user), time()+3600*24*365, "/", ".B域名");
   $this->cookieLoginLocal(urldecode($bts_user));
  } else {
   setcookie("bts_LOGGED_USER", $bts_user, '-1', "/", ".B域名");
   unset($_SESSION['uid']);
  }
 }
 
 function setsession() {
  $bts_user = trim($_POST['bts_user']);
  if(!empty($bts_user) && empty($_SESSION['uid'])) {
   setcookie("bts_LOGGED_USER", urldecode($bts_user), time()+3600*24*365, "/", ".B域名");
   $this->cookieLoginLocal(urldecode($bts_user));
  } else {
   echo 'uuunset';
   setcookie("bts_LOGGED_USER", $bts_user, '-1', "/", ".B域名");
   //unset($_SESSION['uid']);
  }
 }
 
 function cookieLoginLocal($cookieId){
  $cookieId = explode( '.', base64_decode($cookieId) );
  if ($cookieId[0] !== 'baitianshi' || empty($cookieId[1])) {
   return false;
  } else {
   return $this->loginLocal($cookieId[1],false,1);
  }
 }
 
 function loginLocal($uid,$password=false,$isuid=0) {
  $_SESSION['uid'] = $uid;
  return 1;
 }

############# B域名 getbtsck.html #################
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
 <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
 <title>同步登陆</title>
 <script src="/js/jquery-1.8.1.min.js" type="text/javascript"></script>
    <script src="/api/getck"></script>
 <script language="javascript">
 function setck(bts_user) {
  $.post("/api/setsession",{bts_user:bts_user},function(re){ });}
  setck(ck);
    </script>
</head>
<body>
</body>
</html>

该方案尚存在的不足:
当用户直接访问B域时,需要加载一次该页面后,才能判断是否在A域登录,并写入当前域(B域)的SESSION

[!--infotagslink--]

相关文章