From:http://www.cnblogs.com/JinkoWu/p/5056646.htmljavascript
先來講說什麼是單點登陸(SSO)。來自百科的介紹:SSO英文全稱Single Sign On,單點登陸。SSO是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。它包括能夠將此次主要的登陸映射到其餘應用中用於同一個用戶的登陸的機制。它是目前比較流行的企業業務整合的解決方案之一。php
首先想到的單點登陸, 應該就是, 在某個服務器或者站點進行登陸, 登陸以後攜帶登陸ticket, 跳轉到原來登陸的站點。原來登陸的站點經過遠程驗證ticket是否合法。這個方法很容易, 只要全部鏈接都帶上ticket參數, 就能夠不用登陸了。html
這樣單點登陸會有一個侷限性, 好比下例場景(如天貓和淘寶):www.a.cn和www.b.net兩個站點都在瀏覽器中打開了, 兩個站點都未登陸。而後a站點登陸, 再切換到b站點的窗口刷新瀏覽器, 能夠發現b站點依然沒有登陸。由於b站點沒有ticket。java
如何實現呢? 下面有我本身(Jinko)的我的想法:a站點登陸後, 將ticket存到cookie中, 此時a站點是已經登陸的。可是b站點沒法讀取a站點的cookie。由於b站點和a站點是不一樣根域下的, 這個時候咱們應該想到跨域。想到跨域就會想到ajax, canvas多麼蛋疼(canvas在將非當前域的img繪製到畫布中時進行toDataURL時會有安全限制),他們都會由於跨域安全問題而限制了(當 然還有iframe :))。 解決方案就是jsonp, 每當想到跨域我就想到jsonp。沒錯, 它確實爲跨域而生(要否則也沒人用它)。ajax
好, 有了jsonp以上問題就迎刃而解, 思路變成以下:redis
a站點登陸後, b站點經過jsonp獲取a站點的ticket, 寫到當前站點(b)的cookie裏,並執行登陸動做,更新頁面數據。這時候, 後續的全部頁面均可以經過cookie裏的ticket進行遠程驗證。並且從a站點到b站點都無需再url中帶上ticket(除了註銷登陸)。真是方便 多了。a站點註銷登陸就直接從cookie取ticket並將對於ticket對應的數據刪去(通常存於數據庫,或者redis、memcache緩存裏 面)。在下面的例子裏, 爲方便我是存於文件 O(∩_∩)O。數據庫
接下來就是代碼例子, 我用的是php來實現, 代碼結構以下json
index.php爲站點首頁, login.php爲登陸頁面, session.lib.php 是本身實現的session存儲機制, session-api.php是統一登陸接口,.sessioncache目錄存的是session緩存文件。看文件目錄結構至關簡單canvas
要注意的是, 請不要直接用localhost去訪問, 要新建兩個虛擬主機分別用域名www.a.cn和www.b.net。請修改host文件使它們指向127.0.0.1api
如下是代碼:
www.a.cn/index.php:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * by Jinko Wu 5 * Date: 2015/12/17 6 */ 7 error_reporting(E_ALL ^ E_NOTICE); 8 require "session.lib.php"; 9 $session = jsession_start(); 10 11 echo '<meta charset="utf-8"/>'; 12 if(isset($session)) { 13 echo 'A 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.jsession_id().'">退出</a>'; 14 } else { 15 echo 'A 您尚未登陸!'.'<a href="http://www.a.cn/login.php">去登陸</a>'; 16 }
www.a.cn/login.php:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * by Jinko Wu 5 * Date: 2015/12/17 6 */ 7 ?> 8 <meta charset="utf-8"/> 9 <form action="session-api.php?action=login" method="post"> 10 <input type="text" name="name"> 11 <input type="hidden" name="redirect" value="<?php echo $_SERVER['HTTP_REFERER'] ?>"> 12 <input type="submit"> 13 </form>
www.a.cn/session.lib.php:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * by Jinko Wu 5 * Date: 2015/12/17 6 */ 7 function jsession_start() 8 { 9 if(!$_COOKIE['__SESSID']) { 10 jsession_regenerate_id(); 11 } 12 13 return jsession_update(); 14 } 15 16 function jsession_update($session_id=null) 17 { 18 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id; 19 20 if($session_id == '') { 21 return null; 22 } 23 24 if($session_id && $data = jsession_is_valid($session_id)) { 25 jsession_save($data); 26 setcookie('__SESSID', $session_id, time()+jsession_live_time()); 27 return $data; 28 } 29 30 return null; 31 } 32 33 function jsession_regenerate_id() 34 { 35 $sessid = jsession_generate_id(); 36 37 if(jsession_id() != '' && file_exists('.sessioncache/' .jsession_id())) { 38 rename('.sessioncache/' .jsession_id(), '.sessioncache/' .$sessid); 39 } 40 41 $_COOKIE['__SESSID'] = $sessid; 42 setcookie('__SESSID', $sessid, time()+jsession_live_time()); 43 } 44 45 function jsession_generate_id() 46 { 47 return 's'.base_convert(rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999).rand(0, 9999999999), 10, 36); 48 } 49 50 function jsession_id() 51 { 52 return $_COOKIE['__SESSID']; 53 } 54 55 function jsession_live_time() 56 { 57 $gc_maxlifetime = ini_get('session.gc_maxlifetime'); 58 $gc_maxlifetime = $gc_maxlifetime == '' ? 1440 : $gc_maxlifetime; 59 return $gc_maxlifetime; 60 } 61 62 function jsession_is_valid($session_id) 63 { 64 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id; 65 66 if($session_id == '') { 67 return false; 68 } 69 70 if(file_exists('.sessioncache/' .$session_id)) { 71 $data = unserialize(@file_get_contents('.sessioncache/' . $session_id)); 72 return time() <= $data['time'] ? $data['data'] : false; 73 } else { 74 return false; 75 } 76 } 77 78 function jsession_data($session_id=null) 79 { 80 $session_id = $session_id === null ? $_COOKIE['__SESSID'] : $session_id; 81 82 if($session_id == '') { 83 return null; 84 } 85 86 if($data = jsession_is_valid($session_id)) { 87 return $data; 88 } 89 90 return null; 91 } 92 93 function jsession_save($data) 94 { 95 96 if(!is_dir('.sessioncache')) { 97 mkdir('.sessioncache', 0777); 98 } 99 100 if($_COOKIE['__SESSID'] == '') { 101 return null; 102 } 103 104 $file = '.sessioncache/'.$_COOKIE['__SESSID']; 105 $fp = fopen($file , 'w'); 106 107 if(flock($fp , LOCK_EX)) { 108 fwrite($fp, serialize(array('time' => time() + jsession_live_time(), 'data' => $data))); 109 flock($fp, LOCK_UN); 110 } 111 112 fclose($fp); 113 return $data; 114 } 115 116 function jsession_destory($session_id) 117 { 118 if($session_id == '') { 119 return ; 120 } 121 122 if($session_id == $_COOKIE['__SESSID']) { 123 setcookie('__SESSID', ''); 124 $_COOKIE['__SESSID'] = null; 125 } 126 127 if(file_exists('.sessioncache/' .$session_id)) { 128 @unlink('.sessioncache/' .$session_id); 129 } 130 }
www.a.cn/session-api.php:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * by Jinko Wu 5 * Date: 2015/12/17 6 */ 7 error_reporting(E_ALL ^ E_NOTICE); 8 require 'session.lib.php'; 9 10 if($_REQUEST['action'] == 'check') { 11 $session = jsession_is_valid($_REQUEST['id']); 12 echo json_encode(array('session' => $session)); 13 14 } else if($_REQUEST['action'] == 'logout') { 15 if($_REQUEST['sessid'] !== null) { 16 jsession_destory($_REQUEST['sessid']); 17 } 18 19 echo '<meta charset="utf-8"/>'; 20 echo '退出登陸成功, 正在跳轉...'; 21 $_SERVER['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'] == '' ? '/' : $_SERVER['HTTP_REFERER']; 22 echo '<script type="text/javascript">window.location.href = "' . $_SERVER['HTTP_REFERER'] . '";</script>'; 23 24 } else if($_REQUEST['action'] == 'login') { 25 jsession_start(); 26 $data = jsession_save(array('name' => trim($_REQUEST['name']))); 27 $redirect = $_REQUEST['redirect'] ? $_REQUEST['redirect'] : 'http://www.a.cn'; 28 echo '<meta charset="UTF-8"><script type="text/javascript">window.location.href = "'.$redirect.'";</script>'; 29 30 } else { 31 $session = jsession_start(); 32 33 if($session && trim($_REQUEST['call']) != '' && jsession_id() != '') { 34 echo $_REQUEST['call'] . '('.json_encode(array('sessid' => jsession_id(), 'session' => $session)).')'; 35 } 36 }
www.b.net/index.php:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * by Jinko Wu 5 * Date: 2015/12/17 6 */ 7 ?> 8 <meta charset="utf-8"/> 9 <script type="text/javascript"> 10 function setCookie(name,value) 11 { 12 var Days = 30; 13 var exp = new Date(); 14 exp.setTime(exp.getTime() + Days*24*60*60*1000); 15 document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString(); 16 } 17 18 //jsonp 登陸函數 19 function jsonp_do_login(data) 20 { 21 document.getElementById('name').innerHTML = 'B 您好:' + data.session.name + '<a href="http://www.a.cn/session-api.php?action=logout&sessid='+data.sessid+'">退出</a>'; 22 console.log(data); 23 setCookie('__SESSID', data.sessid); 24 } 25 </script> 26 <?php 27 error_reporting(E_ALL ^ E_NOTICE); 28 session_start(); 29 $session = check_session(); 30 $sessid = $_COOKIE['__SESSID']; 31 32 if($session) { 33 echo 'B 您好:' . $session['name'] . '<a href="http://www.a.cn/session-api.php?action=logout&sessid='.$sessid.'">退出</a>'; 34 } else { 35 echo '<span id="name">B 您尚未登陸!<a href="http://www.a.cn/login.php">去登陸</a></span>'; 36 } 37 38 function check_session() 39 { 40 $sessid = $_COOKIE['__SESSID']; 41 $json = file_get_contents("http://www.a.cn/session-api.php?id=$sessid&action=check"); 42 $json_data = json_decode($json, true); 43 44 if($json_data == null || empty($json_data['session'])) { 45 return false; 46 } else { 47 return $json_data['session']; 48 } 49 } 50 51 ?> 52 53 <?php if(!$session): ?> 54 <script type="text/javascript" src="http://www.a.cn/session-api.php?call=jsonp_do_login&<?php echo rand()?>"></script> 55 <?php endif; ?>
點擊這裏下載打包好的代碼: http://files.cnblogs.com/files/JinkoWu/MultiSiteSingleLogin.zip
最後附上一張示例圖片: