我們都知道網易、搜狐等大型門戶都有“通行證”的概念,這個通行證系統就是今天討論的“單點登入系統”。其主要特徵是多個網站一個使用者中心,一點登陸後其他也自動登入,登出也是。比如我們在126登入了郵箱,再去163.com就是登陸狀態。我這裡的實現方案是傳統的cookie方案。希望此文對需要的朋友有用,也希望不足之處大家能夠提出。SSO的準系統:·統一登入所有網站的登入都要跳轉至SSO來登入,同時附帶剛剛請求的url參數,以便登陸後返回。例如 http://sso.a.com/login?url=http://www.b.com ,登入之後,我們Response一個cookie,並且將其domain設為 a.com頂級域,這樣只要是同域的網站都可以直接存取到這個cookie。由於cookie不能跨域,所以這裡要解決不同域下的cookie問題。解決辦法就是通過JS API來擷取a.com的cookie資訊,並通過url傳遞個b.com。我們知道ajax是不能跨域訪問的,但是我們可以<script>一個跨域的JS(這就是JSAPI),所以SSO系統需要提供一個callback參數,輸出為一段js代碼。SSO輸出cookie給調用者,接受callback參數 var user = Request.Cookies["Username"];
var callback = Request.QueryString["callback"];
if(user!=null)
Response.Write(callback+"("+user.ToJson()+")");//ToJson是一個擴充方法,將對象序列化為Json格式b.com擷取cookie的辦法 $.getScript("http://sso.a.com/getcookie?callback=setuser");
function setuser(data)
{
if(data!=null)
{
var user = eval(data);
alert("hello"+user.Username);
}
setcookie(user);//將得到的cookie寫在本地
}通過這種辦法,我們的b.com便可以通過得到的cookie知道該使用者是否登入。·統一登出每個網站登出都必須串連到sso.a.com/logout來實現登出,刪除cookie。·驗證登入狀態了實現同時登出功能,比如bbs.a.com登出了,www.b.com必須知道,所以b.com的所有需要知道登陸狀態的請求都必須訪問一次sso.a.com來驗證是否還在登入。 驗證登入可以有2種辦法:一種是上面提到的通過JSAPI知道cookie是否存在來判斷其他系統是否登出另外一種是在本地已存cookie的情況下通過WebService由服務端去驗證該使用者是否還在登陸狀態。這兩種辦法各有優缺點,第一種是不需要知道本地是否有cookie的,第二種需要藉助webService,但增加的檢驗的嚴謹性。假如我們在b.com做重要的資料操作,最好通過服務端檢驗來判定登陸狀態,如果從cookie來判斷,則必須保證cookie無法偽造,也就是得對cookie進行加密,解密。如果通過WebService用戶端的cookie同樣需要一個無法偽造的標識,比如GUID,但不需要加密,只需要與服務端的GUID和UserName進行匹配即可。但是明顯通過WebService的壓力要遠大於解密cookie。如果網站居多且訪問量很高,那這種驗證請求會對SSO造成巨大壓力。 ·服務端設計如果SSO還有其他壓力大的工作,則需要多台機器部署。多台部署就會有一個Session問題,所以服務端的使用者登入狀態不能把Session存於InProc中,也不能是StatusServer中,只能是SqlServer中。而我的解決辦法是放在分布式緩衝(Memcached)中,當然其實已經不是Session了,而是普通的緩衝列表。把Username和Guid放在Memcached中,做成hash表,能夠很快的找到某個使用者的登陸情況(Guid和請求的一致)·授權白名單 SSO會暴露一些WebService介面給網站使用,所以不能隨便被別人所利用,這時就需要驗證請求的ip是否為授權的地址)
希望有想法的朋友發表評論。