596 votos

ASP.NET MVC - Set custom IIdentity o IPrincipal

Necesito hacer algo bastante simple: en mi ASP.NET aplicación MVC, quiero establecer una costumbre IIdentity / IPrincipal. Lo que es más fácil / más adecuado. Quiero extender el defecto, de modo que yo pueda llamar algo como User.Identity.Id y User.Identity.Role. Nada lujoso, sólo algunas propiedades adicionales.

He leído toneladas de artículos y preguntas, pero me siento como que me estoy haciendo más difícil de lo que realmente es. Pensé que iba a ser fácil. Si un usuario inicia sesión, quiero establecer una costumbre IIdentity. Así que me dije, voy a implementar Application_PostAuthenticateRequest en mi global.asax. Sin embargo, lo que se llama en cada solicitud, y no quiero hacer una llamada a la base de datos en cada petición que iba a solicitud de todos los datos de la base de datos y poner en una costumbre objeto IPrincipal. Que también parece muy innecesario, lento, y en el lugar equivocado (haciendo llamadas de base de datos), pero puedo estar equivocado. O cuando otra cosa sería que los datos provienen de?

Entonces pensé que, cada vez que un usuario inicia la sesión, puedo agregar algunas variables necesarias en mi sesión, que puedo agregar a la costumbre IIdentity en la Application_PostAuthenticateRequest de controlador de eventos. Sin embargo, mi Context.Session es null no, por lo que no es el camino a seguir.

He estado trabajando en esto durante un día y me siento que me estoy perdiendo algo. Esto no debería ser demasiado difícil de hacer, ¿verdad? Yo también estoy un poco confundido por todos el (semi)relacionados con las cosas que vienen con esto. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication .... ¿Soy el único que encuentra todo esto muy confuso?

Si alguien me pudiera decir un simple, elegante y eficiente solución para almacenar algunos datos extra sobre un IIdentity sin todos los extras fuzz.. eso sería genial! Sé que hay otras cuestiones similares sobre LO mismo, pero si la respuesta que necesita está ahí, debo de haber pasado por alto. Gracias.

786voto

LukeP Puntos 5025

He aquí cómo lo hago.

Decidí usar IPrincipal en lugar de IIdentity porque significa que no tengo que implementar ambos IIdentity y IPrincipal.

  1. Crear la interfaz

    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 - para serializar la información personalizada en userdata campo en FormsAuthenticationTicket objeto.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
  4. Método de inicio de sesión - creación de una cookie con información personalizada

    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. Global.asax.cs - Lectura de cookies y sustitución de HttpContext.User objeto, esto se hace por razones imperiosas de 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. Acceso en la máquina de Afeitar vistas

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

y en el código:

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

Creo que el código es auto-explicativo. Si no es así, hágamelo saber.

Además, para hacer el acceso aún más fácil, usted puede crear un controlador de base y anular la devueltos objeto de Usuario (HttpContext.User):

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

y luego, para cada controlador:

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

que le permitirá el acceso personalizado campos en el código como este:

User.Id
User.FirstName
User.LastName

Pero esto no va a funcionar en el interior de las vistas. Para que usted necesita para crear un custom WebViewPage aplicación:

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; }
    }
}

Hacer una página predeterminada tipo de Opiniones/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>

y en las vistas, se puede acceder a ella como esta:

@User.FirstName
@User.LastName

HTH

105voto

John Rasch Puntos 28874

Yo no puedo hablar directamente de ASP.NET MVC, pero para ASP.NET los Formularios Web, el truco es crear un FormsAuthenticationTicket y cifrar en un cookie de una vez el usuario ha sido autenticado. De esta manera, usted sólo tiene que llamar a la base de datos una vez (o AD o lo que sea que usted está utilizando para realizar su autenticación), y la posterior de cada solicitud de autenticar basado en el billete almacenados en la cookie.

Un buen artículo sobre esto: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html

63voto

Aquí es un ejemplo para hacer el trabajo. bool isValid se establece mirando algunos almacén de datos (digamos que su base de datos de usuario). Nombre de usuario es simplemente un IDENTIFICADOR estoy manteniendo. Usted puede agregar información adicional, como la dirección de correo electrónico datos de usuario.

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);
     }
}

en el golbal asax agregue el código siguiente para obtener su información

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;
    }
}

Cuando usted va a utilizar la información más tarde, usted puede tener acceso a su principal personalizado de la siguiente manera.

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

esto le permitirá el acceso personalizado a la información de usuario.

Divertirse, Sriwantha Sri Dice Aravinda

15voto

brady gaster Puntos 745

MVC proporciona la OnAuthorize método que cuelga de su controlador de clases. O, usted podría usar un filtro de acción para realizar la autorización. MVC hace que sea bastante fácil de hacer. He publicado una entrada de blog acerca de esto aquí. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

2voto

Manight Puntos 126

Como una adición a LukeP código de Formularios Web forms de los usuarios (no MVC) si desea simplificar el acceso en el código detrás de sus páginas, basta con agregar el siguiente código a una página base y se derivan de la base de la página en todas las páginas:

Public Overridable Shadows ReadOnly Property User() As CustomPrincipal
    Get
        Return DirectCast(MyBase.User, CustomPrincipal)
    End Get
End Property

Así, en el código detrás de usted, simplemente pueden acceder a:

User.FirstName or User.LastName

Lo que me estoy perdiendo en un Formulario Web escenario, es la forma de obtener el mismo comportamiento que en el código no vinculado a la página, por ejemplo en httpmodules siempre debo agregar a un elenco en cada clase o hay una manera más inteligente de obtener esto?

Gracias por sus respuestas y gracias a LukeP ya he utilizado sus ejemplos como base para mi de usuario personalizado (que ahora ha User.Roles, User.Tasks, User.HasPath(int) , User.Settings.Timeout y muchas otras cosas buenas)

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: