PHP教程:COOKIE与SESSION联合实现跨域
大家都知道SESSION是不可以跨域的,也就是说: A.WEMVC.COM这个域的可执行文件不可以访问到B.WEMVC.COM的SESSION,这个是SESSION的特性,同样也是出于安全角度才这样的.
在一般情况下,一个网站只有一个域名,但是也有些网站架构是由多个子域名组建的.所以就需要SESSION可以跨子域被访问到,这样才可以实现用户的跨域登录.就是说客户在A下登录的,同样B也同时登录了,不需要用户再次登录,同时也实现了参数的跨域传递.当然不可跨域的SESSION本身已经可以帮助我们做很多事情了,那么跨域后的SESSION呢.读到这里是否很激动人心,当然你也可能是正在为SESSION跨域而发愁而找到这篇文章的,同样也祝贺你.我们长话断说了,开始我们今天的课程:COOKIE与SESSION联用实现SESSION跨域.
首先让我们再重新温习下PHP中的COOKIE和SESSION:
COOKIE:
定义:
cookie 常用于识别用户。cookie 是服务器留在用户计算机中的小文件。每当相同的计算机通过浏览器请求页面时,它同时会发送 cookie。通过 PHP,您能够创建并取回 cookie 的值。PS:其中文名叫”曲奇”.
在PHP中用setCookie函数来设置COOKIE,该函数一共有7个参数(在此我要向曾经我面试过的一位同仁道歉,当时我把答案说成了6个,SORRY~,同时我也提醒广大作家尽快更新自己的文章,在PHP5.2.0版本中已经增加为7个参数.),这7个参数分别为 string $name [, string $value [, int $expire [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]]] .
name The name of the cookie. 规定 cookie 的名称。
value The value of the cookie. This value is stored on the clients computer; do not store sensitive information. Assuming the name is ‘cookiename’, this value is retrieved through $_COOKIE['cookiename'] 规定 cookie 的值。
expire The time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch. In other words, you’ll most likely set this with the time() function plus the number of seconds before you want it to expire. Or you might use mktime(). time()+60*60*24*30 will set the cookie to expire in 30 days. If set to 0, or omitted, the cookie will expire at the end of the session (when the browser closes).规定 cookie 的有效期。
Note: You may notice the expire parameter takes on a Unix timestamp, as opposed to the date format Wdy, DD-Mon-YYYY HH:MM:SS GMT, this is because PHP does this conversion internally.
expire is compared to the client’s time which can differ from server’s time.
path The path on the server in which the cookie will be available on. If set to ‘/’, the cookie will be available within the entire domain . If set to ‘/foo/’, the cookie will only be available within the /foo/ directory and all sub-directories such as /foo/bar/ of domain . The default value is the current directory that the cookie is being set in.规定 cookie 的服务器路径。
domain The domain that the cookie is available. To make the cookie available on all subdomains of example.com then you’d set it to ‘.example.com’. The . is not required but makes it compatible with more browsers. Setting it to www.example.com will make the cookie only available in the www subdomain. Refer to tail matching in the » spec for details.规定 cookie 的域名。
secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client. When set to TRUE, the cookie will only be set if a secure connection exists. The default is FALSE. On the server-side, it’s on the programmer to send this kind of cookie only on secure connection (e.g. with respect to $_SERVER["HTTPS"]).规定是否通过安全的 HTTPS 连接来传输 cookie。
httponly When TRUE the cookie will be made accessible only through the HTTP protocol. This means that the cookie won’t be accessible by scripting languages, such as JavaScript. This setting can effectly help to reduce identity theft through XSS attacks (although it is not supported by all browsers). Added in PHP 5.2.0. TRUE or FALSE.规定是否必须通过HTTP协议来定义访问COOKIE,防止XSS攻击.
SESSION全面教程
SESSION在这里就不过多的讲解了,主要是:
- session_cache_expire — Return current cache expire
- session_cache_limiter — Get and/or set the current cache limiter
- session_commit — Alias of session_write_close
- session_decode — Decodes session data from a string
- session_destroy — Destroys all data registered to a session
- session_encode — Encodes the current session data as a string
- session_get_cookie_params — Get the session cookie parameters
- session_id — Get and/or set the current session id
- session_is_registered — Find out whether a global variable is registered in a session
- session_module_name — Get and/or set the current session module
- session_name — Get and/or set the current session name
- session_regenerate_id — Update the current session id with a newly generated one
- session_register — Register one or more global variables with the current session
- session_save_path — Get and/or set the current session save path
- session_set_cookie_params — Set the session cookie parameters
- session_set_save_handler — Sets user-level session storage functions
- session_start — Initialize session data
- session_unregister — Unregister a global variable from the current session
- session_unset — Free all session variables
- session_write_close — Write session data and end session
OK,大概温习了下COOKIE和SESSION,开始实现我们的跨域.
首先我描述下我的思路,COOKIE可以指定域名,也就是说它可以跨域子域,例如:setcookie(’name’,'joshua’,time()+3600*24,’/',’wemvc.com’),那么A.wemvc.com,B.wemvc.com都可以访问到$_COOKIE['name'],值也均为’joshua’.同理,SESSION ID也可以设置成这个域名,那么A.wemvc.com和B.wemvc.com都可以得到同一个SESSION ID,那么我们的目的也就达到了.因为知道了同一个SESSION ID就可以访问到这个SESSION中的值了.SESSION有多种方式存储,文件\数据库\内存等,我们采用数据库存储,因为如果A.wemvc.com,B.wemvc.com不在同一台服务器上,那么内存和文件的存储方式就很难实现跨域了,至于到底又没有方法,本人还没有试过.
首先在数据库中创建一张SESSION表:
CREATE TABLE `sessions` (
`sid` varchar(32) NOT NULL default '',
`expiry` int(20) unsigned NOT NULL default '0',
`value` text NOT NULL,
PRIMARY KEY (`sid`),
KEY `expiry` (`expiry`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
然后写一个类,这个类用于读取\插入\更新\删除以及垃圾回收SESSION
class session{
private $db;
function __construct($db){
$this->db=$db;
}
public function open($save_path,$session_name){
return true;
}
public function close(){
$this->db=null;
return true;
}
public function read($sid){
$rs=$this->db->query("select * from sessions where sid='".$sid."'");
foreach ($rs as $row){
return $row['value'];
}
return null;
}
public function write($sid,$value){
if(is_null($oldvalue=$this->read($sid))){
//insert
return $this->db->query(”insert into sessions (sid,expiry,value)values(’”.$sid.”‘,’”.time().”‘,’”.$value.”‘)”);
}else{
//update
return $this->db->query(”update sessions set expiry=’”.time().”‘,value=’”.$value.”‘ where sid=’”.$sid.”‘”);
}
}
public function destroy($sid){
return $this->db->query(”delete from sessions where sid=’”.$sid.”‘”);
}
public function gc($max_life_time){
return $this->db->query(’delete from sessions where expiry+’.$max_life_time.’<’.time());
}
}
我来解释下这个类:
private $db; 类的DATABASE属性.
function __construct($db) 类的构造函数,在声明类时,可以直接传递DB属性到类中,当然如果还不明白可以先GOOGLE一下”PHP 类 construct 方法”;
public function open($save_path,$session_name) session打开,没有什么花头,直接返回TRUE;
public function close() session关闭,同理open,但注意要关闭DB连接;
public function read($sid) session读取,传值SID,在数据表中将这个SID的VALUE作为返回值返回;
public function write($sid,$value) session的写入与更新,这个你会有疑问,为什么set expiry=’”.time().”‘,稍后答案在清空过期SESSION GC方法中便会揭晓;
public function destroy($sid) session的销毁,很简单,就是把数据表中等于这个SID的数据删除掉;
public function gc($max_life_time) 清空过期session,把超过max_life_time的SESSION都销毁掉,也就是SESSION的创建时间加上最大生存时间小于现在时间( expiry+’.$max_life_time.’<’.time())的SESSION数据删除掉,这下你会明白为什么在写入和更新SESSION的方法中为什么写当前时间了吧,当然这个写法不是绝对的,随个人意愿只要你的SQL写正确了,也就可以了.
好我们接着来看更重要的部分:
上面的类中需要一个数据库链接属性,所以声明对象的时候需要这样:
$session=new session(your db connect adapter);
数据库链接我可以提供大家一个PDO的方法,参照使用:
function connect_db($arrPDODB){
$db=new PDO($arrPDODB['db_driver'].’:host=’.$arrPDODB['db_host'].’;dbname=’.$arrPDODB['db_name'],$arrPDODB['db_user'],$arrPDODB['db_password']);
$db->query(”set names ‘utf8′”);
return $db;
}
SO,上面声明对象部分你可以这样写:
$session=new session(connect_db($arrPDODB));
接下来:
//设置色session id的名字
ini_set('session.name', 'sid');
//不使用 GET/POST 变量方式
ini_set('session.use_trans_sid', 0);
//设置垃圾回收最大生存时间
ini_set('session.gc_maxlifetime', 3600);
//使用 COOKIE 保存 SESSION ID 的方式
ini_set('session.use_cookies', 1);
ini_set('session.cookie_path', '/');
//多主机共享保存 SESSION ID 的 COOKIE,注意此处域名为一级域名
ini_set('session.cookie_domain', ' wemvc.com');
//将 session.save_handler 设置为 user,而不是默认的 files
session_module_name('user');
session_set_save_handler(array($session,'open'),
array($session,'close'),
array($session,'read'),
array($session,'write'),
array($session,'destroy'),
array($session,'gc'));
以上都是SESSION的设置,不明白的多搜索下手册,我喜欢刨根究底这样的学习方式,这样你可以学透一个知识点,而不是知道一知半解,就认为自己懂了或者会了.
最后在你需要的地方将SESSION启动:
session_start();
最后再提供一个如何防止Session伪造攻击的博文,希望能够仔细阅读.
好了,大功告成,只要在每个执行文件之前包含这个类,并启动它,你的程序可以跨域了,呵呵.当然也可以跨一级域名.你可以在我写出下一篇博文之前自己先研究下啊.
另外,先说一句,其实AJAX也可以跨子域,当时给我下一篇博文作铺垫了.哈哈,欢迎大家与我一同讨论学习.
2008年9月26日更新:
新增了一个demo程序,程序很简单,没有写入库的东西,适合新手学习.
附:
demo_session_1
程序讲解:很简单的 a b 两个文件夹代表你两个域,把你的虚拟机设置为a.yourdomain.com、b.yourdomain.com就可以了。先把这个搞懂,再考虑session入库的事情。session入库主要是因为一个网站有多台服务器的情况下,若session还是以默认的文件型保存的话,多服务器是行不通的。
demo_session 2
程序讲解:这份程序添加了入库功能,数据表的创建,文件上面有的。