前年我寫過一篇隨筆抱怨microsoft在asp.net架構(gòu)中session_end事件上處理,說來慚愧,其實(shí)當(dāng)年我對(duì)asp.net運(yùn)行時(shí)的復(fù)雜性理解不足。實(shí)話說,捕捉通過身份驗(yàn)證和注銷身份驗(yàn)證對(duì)我來說,意義重大。例如:
在登錄前先檢查是否已經(jīng)存在sso提供器;
登錄完成后加載相關(guān)的權(quán)限,這些加載過程可能與具體應(yīng)用項(xiàng)目完全無(wú)關(guān);
登錄結(jié)束后通知sso提供器清除cookie內(nèi)容;
......
目前的asp.net提供的解決方案是在global.cs中加上formsauthentication_onauthenticated方法來捕捉已通過驗(yàn)證事件。該方法的缺陷是:
1.只能捕捉forms身份驗(yàn)證方式,而不能捕捉windows和passport認(rèn)證方式;
2.只能捕捉已通過身份驗(yàn)證事件而不能捕捉身份注銷事件;
3.必須修改global.cs文件。
以上任何一個(gè)缺陷都是我無(wú)法接受的。當(dāng)時(shí)在asp.net1.1解決那個(gè)問題時(shí)用了五六個(gè)接口,十多個(gè)類,并且有一個(gè)輸出類要求應(yīng)用程序登入和注銷時(shí)訪問相應(yīng)的方法,而不是自由地使用formsauthentication的相關(guān)方法。現(xiàn)如今該問題總算比較滿意地解決了。思路是這樣的:
在一個(gè)httpmodule中建立兩張會(huì)話表,一張記錄已通過身份驗(yàn)證的會(huì)話;另一張記錄未通過身份驗(yàn)證的會(huì)話,這樣,在httpapplication.acquirerequeststate事件中查找每個(gè)會(huì)話在這兩張表中的狀態(tài):
狀態(tài)一 兩張表中都沒有 這是一個(gè)新的會(huì)話
狀態(tài)二 在已通過身份驗(yàn)證的會(huì)話表中 已通過身份驗(yàn)證
狀態(tài)三 在未通過身份驗(yàn)證的會(huì)話表中 未通過身份驗(yàn)證
如果是狀態(tài)一,則立即調(diào)用所有sso提供器的身份查驗(yàn)方法,只要有任何一個(gè)sso提供器證實(shí)已經(jīng)通過了身份驗(yàn)證,則立即將狀態(tài)調(diào)整到狀態(tài)二,并通知所有訂閱身份狀態(tài)變化的handler。如果是狀態(tài)二或狀態(tài)三,則立即與會(huì)話的實(shí)際身份狀態(tài)進(jìn)行比對(duì)。會(huì)話實(shí)際的身份狀態(tài)可以通過查詢httpcontext.user來獲得。如果二者不同,則根據(jù)情況調(diào)整表中所記錄的狀態(tài),并向訂閱身份變化的handler發(fā)出相應(yīng)的通知。
有一個(gè)問題是:會(huì)話列表的查詢頻度非常高,每次request都不可避免查詢一次。所以這里對(duì)算法的選擇要求較高。我在實(shí)際的項(xiàng)目中選擇了字符串?dāng)?shù)組的binarysearch算法。這樣每次添加或刪除新的會(huì)話時(shí)不可避免對(duì)字符串在數(shù)組中的位置進(jìn)行調(diào)整,以保持排序狀態(tài)。當(dāng)然,在比對(duì)過程中也需要根據(jù)命中率調(diào)整比對(duì)順序,例如三種狀態(tài)中,顯然狀態(tài)二的比例最高(當(dāng)然數(shù)組往往也最龐大),應(yīng)該優(yōu)先選擇。
最后的解決方案是:只用了三個(gè)接口,一個(gè)httpmodule和幾個(gè)內(nèi)部類就實(shí)現(xiàn)了,完全不必修改global.cs,且沒有任何輸出類供登錄認(rèn)證模塊調(diào)用,所有的sso提供器也只需要通過web.config來配置,對(duì)業(yè)務(wù)層是完全透明的。這三個(gè)接口是:一個(gè)配置參數(shù)上下文接口、一個(gè)sso提供器接口(同時(shí)兼做捕捉身份狀態(tài)變化的handler接口)、一個(gè)handler接口的工廠接口(以保持handler接口的構(gòu)造器自由以及決定是否建立handler接口的實(shí)現(xiàn)類實(shí)例)。
新聞熱點(diǎn)
疑難解答
圖片精選