2010-09-21 116 views
3

我希望能夠訪問經過身份驗證的用戶(如UserId和FirstName)的自定義屬性,而不必每次查詢數據庫。我發現this site在Stack Overflow上發表了一篇文章,我喜歡這種方法 - 但是我使用了IoC/repositories,並決定不嘗試使用global.asax來與數據庫進行通信,因爲擔心它會與存儲庫模式不兼容。ASP.NET MVC中的自定義主體

取而代之,我創建了一個CustomPrincipal的接口,並使用IoC(Castle)創建一個實例並將其傳遞給控制器​​(並隨後傳遞給我的基本控制器)。

基本控制器使用我在CustomPrincipal中創建的方法來實現博客作者在global.asax中提到的相同任務。即,CustomPrincipal從數據庫或緩存初始化並分配給HttpContext.Current.User。

我的控制器/視圖可以然後引用屬性如下......

((ICustomPrincipal)(HttpContext.Current.User)).FirstName; 

它的工作原理,但我感應有些代碼味道。首先,如果我從控制器中引用HttpContext,我已經殺死了我的單元測試。我正在考慮修改我的CustomPrincipal對象以返回上面的值(這樣我可以在我的單元測試中嘲笑它),但我想知道這是一種解決方法,而不是一個好的解決方案。

我對此有何看法?我可以做些微小的調整來使它成爲一個可靠的解決方案,還是我應該從零開始使用FormsAuthenticationTicket或類似的東西來實現這種效果?

謝謝!

回答

2

創建ICustomPrincipalManager接口如何?

public interface ICustomPrincipalManager 
{ 
    ICustomPrincipal Current {get;} 
} 

它可以通過訪問類的HttpContext,數據庫,緩存,或什麼來實現,但你也可以嘲笑的單元測試的接口。你的控制器將使用IoC框架,讓您ICustomPrincipalManager,然後訪問信息是這樣的:

_customPrincipalManager.Current.FirstName 
+0

這聽起來像一個好主意 - 我打算玩這個,看看我能不能做到。謝謝! – Mayo 2010-09-21 18:33:12

+0

沒問題。如果遇到任何障礙,請告訴我。 – StriplingWarrior 2010-09-21 18:55:46

7

我想拋出一個替代的想法只是讓人們尋找這些信息可以有一些選擇。

我去尋找一個可行的FormsAuthenticationTicket示例,發現NerdDinner做一個相當不錯的工作,添加自定義屬性而不影響單元測試。

在我的情況下,我修改了我的LogOn例程(我已經在單元測試中嘲笑)來創建一個FormsAuthenticationTicket。 NerdDinner加密票證並將其添加爲cookie,但我也可以將加密票證添加到緩存中,如original proposal。我還使用代表我所有自定義屬性的JSON序列化對象替換了單個UserData屬性。

CustomIdentityDTO dto = new CustomIdentityDTO { 
    UserId = userId, FirstName = firstName, LastName = lastName }; 
JavaScriptSerializer serializer = new JavaScriptSerializer(); 

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
    1, // version 
    username, 
    DateTime.Now, // creation 
    DateTime.Now.AddMinutes(30), // expiration 
    false, // not persistent 
    serializer.Serialize(dto)); 

string encTicket = FormsAuthentication.Encrypt(authTicket); 
//HttpContext.Current.Response.Cookies.Add(...) 
HttpContext.Current.Cache.Add(username, encTicket, ... 

然後我檢索加密票證(從超高速緩存或Cookie)通過PostAuthenticateRequest處理程序很像的NerdDinner在Global.asax中(用於餅乾)或博客的建議(高速緩存)。

NerdDinner實現IIdentity而不是IPrincipal。代碼中自定義字段的引用如下:

((CustomIdentity)Page.User.Identity).FirstName // from partial view 

((CustomIdentity)User.Identity).FirstName // from controller 

使用這兩種方法後,我發現NerdDinner的方法工作得很好。切換後我沒有遇到太多的障礙。