天天看點

架構之路(八)從CurrentUser說起

CurrentUser,也就是目前使用者,這是我們系統中大量使用的一個概念。

确認目前使用者

當然,我們利用的是cookie:使用者的ID存放在cookie中,伺服器端通過cookie中的Id,查找資料庫,得到需要的使用者資訊。

那麼,這裡就有一個安全問題,如何防止cookie的僞造或篡改?我們采用了以下方法:

首先,cookie中除了存放使用者Id,還存放了一個加密過後的驗證碼,其來源如下:

未加密的驗證碼在使用者生成時由系統随機産生,并存儲在資料庫中,如:287653;

它會被使用MD5加密成我們看不懂的字元串,如:<code>49b5f37dff119cf81fcb2b4e6077e17;</code>

是以,當伺服器端使用cookie中的使用者Id時,會先檢查加密過後的驗證碼是否有效。捏造的驗證碼是不會通過稽核的。

還有一點需要說明的是,我們不考慮一個有效的cookie(連同驗證碼)被盜竊的情形。因為這就相當于你的電腦被别人使用了一樣,我們确實無法判斷使用你電腦的是不是你本人。

為什麼沒有使用session

可能有同學會想到,每次取cookie再查資料庫,是不是會增加資料庫負擔,為什麼不考慮session呢?兩個方面的原因:

session有定時清理機制。不管時間長短,session總有可能被清理掉的時候,這個時候不能讓使用者再重新登入啊!多麻煩,是不是?你可以if(session["userInfo"]== null),再通過cookie取資料再裝到session裡,但何苦呢?

session難以同步更新,維護起來非常麻煩。比如目前使用者發表一篇文章,積分增加了,你就得既改session又改資料庫,這個同步過程是比較容易出問題的。

上面兩個問題,NHiernate的cache已經做得很好了,不會增加資料庫負擔,這個以後會講。

CurrentUser的ViewModel

CurrentUser最麻煩的一件事情是:很多頁面是根據不同的目前使用者,顯示不同的内容的。以“任務編輯”頁面為例,目前使用者是該任務的釋出人,釋出欄可編輯;否則,釋出欄僅僅是可讀的。

是以,最初我們的方案很簡單,也封裝一個CurrentUserModel就可以了呀!

但後來我們發現:

需要判斷的東西越來越多,比如還要判斷目前使用者是不是管理者、目前使用者有沒有驗收權限、目前使用者的上一次操作……把這些所有的資訊都裝到一個ViewModel裡肯定是不合适的。怎麼辦呢?想到的自然就是拆分類,但CurrentUser還怎麼拆分呢?

頁面的判斷邏輯也變得複雜起來,比如目前使用者有沒有某種權限得查他的申請曆史和準許情況,并且還得看目前文章是那種類型及其作者的權限等。這些大段大段的邏輯就寫在View裡面麼?關鍵是有些資料是單個View取不到的,需要從其他地方(比如url parameter中)擷取,這些都進一步的增加了複雜性。讓我們不得不考慮,我們是不是應該把這些邏輯移到Controller中,然後直接将結果告訴View,保持View的幹淨清爽?

在MVC架構中,Controller将Model傳遞給View,其實可能有兩種情況:

View直接呈現Model的資料,比如直接顯示CurrentUser的使用者名

View還可以利用Model中的資料進行運算,然後予以呈現,比如比較CurrentUser和目前任務的承接人

我曾經計劃禁止掉第2種情形,也就是說:在View裡面不需要任何計算,隻負責呈現。用代碼表示就是:

而不是之前的:

但我們最終放棄了,因為實作起來太臃腫了。我們可以想象,這樣的話,我們首先就至少需要三個Is屬性:

有點怪,但好像還可以接受,但後來情況發生了變化,我們還得考慮目前使用者即是釋出人又是承接人,或者即是承接人又是驗收人,或者既是……又是……的情形:

這代碼給人的感覺就是有病了。關鍵是,誰知道以後還來不來一個“是…和…但不是……”的邏輯呢?到時候又該怎麼辦呢?

是以,取巧是不行了,我們還是得面對這個問題:

如何劃分Controller和View之間的邏輯/責任?

更直白一點的講,哪些事該Controller做,哪些事該View做?這個問題真的超級虐心。我想來想去,隻能說:“能Controller做的,盡量讓Controller做”。我自己對這個問題都相當不滿意,但實在是沒有辦法啦。

具體到CurrentUser的ViewModel,我們提出以下兩個原則:

不包含需要和其他對象互動運算才能得到的資料,比如目前使用者是不是目前任務的釋出人,需要和“目前任務的釋出人”做比較,就不能包含進來

隻能是需要多個View共用的資料,才能放進來。比如使用者名,很多View都需要,就放進來好了。

為什麼需要明确這些原則

可能你耐着性子看了上面的分析,最後卻隻得到一個似是而非又蛋疼的原則,會忍不住的問,“為什麼一定需要/講解這些原則?讓程式員根據實際情況,自由發揮,不行麼?”

淺層次的原因是要保證代碼的可讀性。閱讀别人的代碼是一件非常累的事情。但如果所有的代碼都像一個人寫的,而且這個人的思路自始至終都是非常清晰的,這樣,我們會稍稍輕松一點。代碼不是文學作品,在絕大多數情況下,不能天馬行空自由發揮!

我們很多開發人員都已經開始注意代碼的規範,但大多數還停留在縮進、換行、命名之類的細節(當然,這些也很重要)上;而架構師應站在一個更全局的高度,來“規範”所有的開發行為。

是以,其實更深層次的原因是:所有的代碼都必須規範化。既然要規範化,那麼首先就要有規範!先可以不管好壞,但至少要有。那麼怎麼制定完善這個規範呢?我分享一下我的經驗:

按規範文檔,做入職教育訓練,教育訓練可以着重講道理,強化開發人員代碼規範化的思維;

所有代碼都必須review。review要往“挑刺”的方向靠,是以不規範的代碼其實是很容易被發現的;

開發人員不服review的結果,review的人員要拿出依據(規範文檔)來;

規範文檔中如果還沒有相關的規定,立即補充,并照此執行,包括改正以前不合規範的代碼

這樣不斷的疊代,基本上就能不斷的提高代碼的規範性,并得到一份不錯的規範文檔。

好像寫跑題了,又是項目管理方向的東西。就先這樣吧!前台的架構,想想,剩下的應該就是單元測試(都還沒做,是以暫時也講不了),還有可能其他一些細節了,以後查漏補缺吧。接下來希望參與到項目的前台開發的同學就可以開始聯系我了。部落格系列我們将接着講Service層。