3

Je souhaite pouvoir accéder à des propriétés personnalisées pour un utilisateur authentifié tel que UserId et FirstName sans interroger la base de données à chaque fois. J'ai trouvé this site à travers un post sur Stack Overflow et j'aime l'approche - mais j'utilise IoC/repositories et ai décidé de ne pas essayer et obtenir global.asax pour communiquer avec la base de données de peur qu'elle serait incompatible avec le modèle de dépôt. Au lieu de cela, j'ai créé une interface pour CustomPrincipal et j'utilise IoC (Castle) pour créer une instance et la transmettre aux contrôleurs (et par la suite à mon contrôleur de base).Principal personnalisé dans ASP.NET MVC

Le contrôleur de base utilise des méthodes que j'ai créées dans CustomPrincipal pour réaliser la même tâche que l'auteur du blog a traitée dans global.asax. À savoir, CustomPrincipal est initialisé à partir de la base de données ou du cache et affecté à HttpContext.Current.User.

Mes contrôleurs/vues peuvent ensuite référencer les propriétés comme suit ...

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

Il fonctionne, mais je sens une odeur de code. Tout d'abord, si je référence HttpContext des contrôleurs, j'ai tué mes tests unitaires. Je pense à modifier mon objet CustomPrincipal pour retourner la valeur ci-dessus (de sorte que je puisse la mocker dans mes tests unitaires) mais je me demande si c'est une solution de contournement par opposition à une bonne solution.

Est-ce que je vais dans ce sens? Y at-il des ajustements mineurs que je pourrais faire pour en faire une solution robuste ou devrais-je commencer à partir de zéro avec FormsAuthenticationTicket ou quelque chose à cet effet?

Merci!

Répondre

2

Qu'en est-il de la création d'une interface ICustomPrincipalManager?

public interface ICustomPrincipalManager 
{ 
    ICustomPrincipal Current {get;} 
} 

Il peut être mis en œuvre par une classe qui accède HttpContext, la base de données, la mise en cache, ou autre chose, mais vous pouvez aussi simuler l'interface pour les tests unitaires. Vos contrôleurs utiliseraient le cadre IoC pour obtenir votre ICustomPrincipalManager, et ensuite accéder à des informations comme ceci:

_customPrincipalManager.Current.FirstName 
+0

Cela semble être une bonne idée - je vais jouer avec ça pour voir si je peux le faire fonctionner. Merci! – Mayo

+0

Pas de problème. Faites-moi savoir si vous rencontrez des problèmes. – StriplingWarrior

7

Je voulais jeter une autre idée pour que les gens qui cherchent ces informations peuvent avoir des choix. Je suis allé chercher un exemple de FormsAuthenticationTicket viable et ai trouvé que NerdDinner fait un travail assez décent en ajoutant des propriétés personnalisées sans impact sur les tests unitaires.

Dans mon cas, j'ai modifié ma routine LogOn (que je me moquais déjà dans mes tests unitaires) pour créer un FormsAuthenticationTicket. NerdDinner crypte le ticket et l'ajoute en tant que cookie, mais je suis également capable d'ajouter le ticket crypté au cache comme le original proposal. J'ai également remplacé la propriété UserData unique par un objet sérialisé JSON représentant toutes mes propriétés personnalisées.

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

Puis-je récupérer le ticket crypté (soit à partir du cache ou les cookies) dans global.asax par un gestionnaire PostAuthenticateRequest beaucoup comme NerdDinner (pour biscuits) ou la proposition du blogueur (pour le cache). NerdDinner implémente IIdentity au lieu de IPrincipal.

Les références aux champs personnalisés dans le code sont les suivantes:

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

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

Après avoir travaillé avec les deux méthodes, je trouve que l'approche de NerdDinner fonctionne très bien. Après avoir basculé, je n'ai pas rencontré beaucoup d'obstacles.