661 votes

ASP.NET MVC - Set IIdentity ou IPrincipal personnalisé

J'ai besoin de faire quelque chose d'assez simple: dans mon ASP.NET application MVC, je veux personnaliser l'Identité / IPrincipal. Selon ce qui est le plus facile / plus approprié. Je veux étendre la valeur par défaut de sorte que je peux appeler quelque chose comme User.Identity.Id et User.Identity.Role. Rien de compliqué, juste quelques propriétés supplémentaires.

J'ai lu des tonnes d'articles et de questions, mais je me sens comme je suis rendant la tâche plus difficile qu'elle ne l'est en réalité. Je pensais que ce serait facile. Si un utilisateur se connecte, je veux personnaliser l'Identité. Alors j'ai pensé, je vais implémenter Application_PostAuthenticateRequest dans mon global.asax. Cependant, qui est appelé à chaque demande, et je ne veux pas faire un appel à la base de données à chaque requête qui en ferait la demande, toutes les données de la base de données et le mettre dans un objet IPrincipal personnalisé. Qui semble également très inutiles, lent, et au mauvais endroit (faire des appels de base de données) mais je peux me tromper. Ou là où d'autre aurait que viennent les données?

J'ai donc pensé que, chaque fois qu'un utilisateur se connecte, je peux ajouter quelques variables dans ma session, à laquelle je rajoute à la coutume l'Identité dans l' Application_PostAuthenticateRequest de gestionnaire d'événements. Cependant, mon Context.Session est null il y a, donc, qui est également pas la voie à suivre.

J'ai travaillé sur ce projet un jour maintenant et je sens que je suis en manque de quelque chose. Cela ne devrait pas être trop difficile à faire, non? Je suis aussi un peu confus par tous les (semi)liés à des trucs qui vient avec. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication.... Suis-je le seul qui trouve tout cela très confus?

Si quelqu'un pouvait m'en dire un simple, élégant et efficace solution pour stocker quelques données supplémentaires sur une Identité sans tout le fuzz.. ce serait génial! Je sais qu'il y a des questions similaires sur de SI, mais si la réponse dont j'ai besoin est là, je l'ai oublié. Merci.

850voto

LukeP Points 5025

Voici comment je le fais.

J'ai décidé d'utiliser IPrincipal au lieu de l'Identité, car elle signifie que je n'ai pas à mettre en œuvre à la fois l'Identité et IPrincipal.

  1. Créer l'interface

    interface ICustomPrincipal : IPrincipal
    {
        int Id { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
    }
    
  2. CustomPrincipal

    public class CustomPrincipal : ICustomPrincipal
    {
        public IIdentity Identity { get; private set; }
        public bool IsInRole(string role) { return false; }
    
        public CustomPrincipal(string email)
        {
            this.Identity = new GenericIdentity(email);
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
  3. CustomPrincipalSerializeModel - pour la sérialisation personnalisée de l'information dans userdata champ dans l'objet FormsAuthenticationTicket.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
  4. Méthode d'ouverture de session - mise en place d'un cookie avec des informations personnalisées

    if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
    {
        var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
    
        CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
        serializeModel.Id = user.Id;
        serializeModel.FirstName = user.FirstName;
        serializeModel.LastName = user.LastName;
    
        JavaScriptSerializer serializer = new JavaScriptSerializer();
    
        string userData = serializer.Serialize(serializeModel);
    
        FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                 1,
                 viewModel.Email,
                 DateTime.Now,
                 DateTime.Now.AddMinutes(15),
                 false,
                 userData);
    
        string encTicket = FormsAuthentication.Encrypt(authTicket);
        HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
        Response.Cookies.Add(faCookie);
    
        return RedirectToAction("Index", "Home");
    }
    
  5. Mondiale.asax.cs - Lecture de cookie et le remplacement de HttpContext.L'utilisateur de l'objet, c'est faire en substituant PostAuthenticateRequest

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    
        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
    
            CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
    
            CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
            newUser.Id = serializeModel.Id;
            newUser.FirstName = serializeModel.FirstName;
            newUser.LastName = serializeModel.LastName;
    
            HttpContext.Current.User = newUser;
        }
    }
    
  6. L'accès au Rasoir vues

    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)
    

et dans le code:

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

Je pense que le code est auto-explicatif. Si ce n'est pas le cas, faites le moi savoir.

En plus de rendre l'accès plus facile, vous pouvez créer un contrôleur de base et remplacer le retour de l'objet Utilisateur (HttpContext.L'utilisateur):

public class BaseController : Controller
{
    protected virtual new CustomPrincipal User
    {
        get { return HttpContext.User as CustomPrincipal; }
    }
}

et puis, pour chaque contrôleur:

public class AccountController : BaseController
{
    // ...
}

qui vous permettra d'accéder à des champs personnalisés dans le code comme ceci:

User.Id
User.FirstName
User.LastName

Mais cela ne fonctionne pas à l'intérieur de points de vue. Pour cela, vous devez créer un personnalisé WebViewPage mise en œuvre:

public abstract class BaseViewPage : WebViewPage
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

Faire une page par défaut de type dans les Vues/web.config:

<pages pageBaseType="Your.Namespace.BaseViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />
  </namespaces>
</pages>

et dans les vues, vous pouvez y accéder comme ceci:

@User.FirstName
@User.LastName

HTH

110voto

John Rasch Points 28874

Je ne peux pas parler directement pour ASP.NET MVC, mais pour ASP.NET des Formulaires Web, l'astuce est de créer un FormsAuthenticationTicket et les chiffrer dans un cookie une fois que l'utilisateur a été authentifié. De cette façon, vous n'avez qu'à appeler la base de données une fois (ou AD ou ce que vous utilisez pour effectuer votre authentification), et à chaque demande ultérieure, va authentifier basé sur le billet stocké dans le cookie.

Un bon article à ce sujet: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html

63voto

Voici un exemple pour faire le travail. bool isValid est en regardant une banque de données (permet de dire que l'utilisateur de votre base de données). UserID est juste une ID je suis maintenir. Vous pouvez ajouter des renseignements supplémentaires comme l'adresse email de l'utilisateur des données.

protected void btnLogin_Click(object sender, EventArgs e)
{         
    //Hard Coded for the moment
    bool isValid=true;
    if (isValid) 
    {
         string userData = String.Empty;
         userData = userData + "UserID=" + userID;
         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
         string encTicket = FormsAuthentication.Encrypt(ticket);
         HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
         Response.Cookies.Add(faCookie);
         //And send the user where they were heading
         string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
         Response.Redirect(redirectUrl);
     }
}

dans le golbal asax ajoutez le code suivant à la restauration de vos informations

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = 
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

Lorsque vous allez utiliser l'information plus tard, vous pouvez accéder à votre tableau principal comme suit.

(CustomPrincipal)this.User
or 
(CustomPrincipal)this.Context.User

cela vous permettra de l'accès personnalisé à l'utilisateur de l'information.

Amusez-vous, Sriwantha Sri Aravinda

16voto

brady gaster Points 745

MVC fournit la méthode OnAuthorize qui pend de vos classes de contrôleur. Ou, vous pouvez utiliser un filtre d’action personnalisée pour exécuter une autorisation. MVC le rend assez facile à faire. J’ai posté un billet de blog sur ce ici. http://www.bradygaster.com/post/Custom-Authentication-with-MVC-3.0

11voto

Baseless Points 71

Voici une solution si vous avez besoin de brancher jusqu'à certaines méthodes pour le @de l'Utilisateur pour une utilisation dans vos vues. Pas de solution pour toute adhésion à la personnalisation, mais si la question d'origine était nécessaire pour les vues seul, ce serait peut-être suffisant. Le ci-dessous a été utilisé pour vérifier une variable retournée à partir d'un authorizefilter, utilisée afin de vérifier si certains liens wehere à être présentées ou pas(pour tout type de logique d'autorisation d'accès ou d'octroi).

using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Security.Principal;

    namespace SomeSite.Web.Helpers
    {
        public static class UserHelpers
        {
            public static bool IsEditor(this IPrincipal user)
            {
                return null; //Do some stuff
            }
        }
    }

Puis il suffit d'ajouter une référence dans les domaines du web.config, et de l'appeler comme ci-dessous dans la vue.

@User.IsEditor()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X