大家都知道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程序講解:這份程序添加了入庫功能,數據表的創建,文件上面有的。