3

I want to be able to access custom properties for an authenticated user like UserId and FirstName without querying the database each time. I found this site through a post on Stack Overflow and I like the approach - but I use IoC / repositories and decided not to try and get global.asax to communicate with the database for fear that it would be incompatible with the repository pattern.

Instead, I created an interface to CustomPrincipal and I use IoC (Castle) to create an instance and pass it to the controllers (and subsequently to my base controller).

The base controller uses methods I created in CustomPrincipal to achieve the same task that the blog author addressed in global.asax. Namely, CustomPrincipal is initialized from the database or cache and assigned to HttpContext.Current.User.

My controllers/views can then reference the properties as follows...

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

It works, but I'm sensing some code smells. First and foremost, if I reference HttpContext from the controllers I've killed my unit testing. I'm thinking about modifying my CustomPrincipal object to return the above value (such that I can mock it in my unit tests) but I'm wondering if this is a workaround as opposed to a good solution.

Am I going about this the right way? Are there minor tweaks I could do to make it a robust solution or should I start from scratch with FormsAuthenticationTicket or something to that effect?

Thanks!

4

2 に答える 2

7

この情報を探している人々がいくつかの選択肢を持つことができるように、私は別のアイデアを捨てたかった.

実行可能な FormsAuthenticationTicket の例を探しに行ったところ、NerdDinnerが単体テストに影響を与えずにカスタム プロパティを追加するのにかなり適切な仕事をしていることがわかりました。

私の場合、LogOn ルーチン (単体テストで既にモックしていたもの) を変更して、FormsAuthenticationTicket を作成しました。NerdDinner はチケットを暗号化して Cookie として追加しますが、元の提案のように暗号化されたチケットをキャッシュに追加することもできます。また、単一の UserData プロパティを、すべてのカスタム プロパティを表す JSON シリアル化オブジェクトに置き換えました。

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, ...

次に、NerdDinner (Cookie の場合) またはブロガーの提案 (キャッシュの場合) とよく似た PostAuthenticateRequest ハンドラーを介して、global.asax 内の暗号化されたチケットを (キャッシュまたは Cookie から) 取得します。

NerdDinner は、IPrincipal の代わりに IIdentity を実装します。コード内のカスタム フィールドへの参照は次のとおりです。

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

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

両方の方法で作業した後、NerdDinner のアプローチが非常にうまく機能することがわかりました。切り替えた後、私は障害物にあまり遭遇しませんでした。

于 2010-09-22T13:47:31.703 に答える
2

ICustomPrincipalManagerインターフェイスを作成するのはどうですか?

public interface ICustomPrincipalManager 
{
    ICustomPrincipal Current {get;}
}

HttpContext、データベース、キャッシングなどにアクセスするクラスによって実装できますが、単体テスト用にインターフェースをモックすることもできます。コントローラはIoCフレームワークを使用してICustomPrincipalManagerを取得し、次のような情報にアクセスします。

_customPrincipalManager.Current.FirstName
于 2010-09-21T18:26:32.687 に答える