asp.net mvc - tables - Cómo ampliar las propiedades disponibles de User.Identity
asp.net identity web api (6)
Estoy usando MVC5 Identity 2.0 para que los usuarios inicien sesión en mi sitio web, donde los detalles de autenticación se almacenan en una base de datos SQL. Asp.net Identity se ha implementado de manera estándar, como se puede encontrar en muchos tutoriales en línea.
La clase ApplicationUser en IdentityModels se ha ampliado para incluir algunas propiedades personalizadas, como un OrganizationId entero. La idea es que se pueden crear y asignar muchos usuarios a una Organización común para fines de relación con la base de datos.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
//Extended Properties
public DateTime? BirthDate { get; set; }
public long? OrganizationId { get; set; }
//Key Mappings
[ForeignKey("OrganizationId")]
public virtual Organization Organization { get; set; }
}
¿Cómo puedo recuperar la propiedad OrganizationId del usuario actualmente conectado desde un controlador? ¿Está disponible a través de un método una vez que un usuario ha iniciado sesión o siempre tengo que recuperar el OrganizationId de la base de datos, basado en el UserId, cada vez que se ejecuta un método de controlador?
Al leer en la web que he visto, necesito usar lo siguiente para iniciar sesión en UserId, etc.
using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();
Sin embargo, OrganizationId no es una propiedad disponible en User.Identity. ¿Necesito extender User.Identity para incluir la propiedad OrganizationId? Si es así, ¿cómo hago para esto?
La razón por la que necesito el OrganizationId con tanta frecuencia es que muchas consultas de tabla dependen del OrganizationId para recuperar datos relevantes para la Organización que está asociada al usuario conectado.
Dhaust ofrece una buena forma de agregar la propiedad a la clase ApplicationUser. Mirando el código OP, parece que pueden haber hecho esto o estaban en camino de hacerlo. La pregunta pregunta
¿Cómo puedo recuperar la propiedad OrganizationId del usuario actualmente conectado desde un controlador? Sin embargo, OrganizationId no es una propiedad disponible en User.Identity. ¿Necesito extender User.Identity para incluir la propiedad OrganizationId?
Pawel ofrece una forma de agregar un método de extensión que requiere el uso de declaraciones o agregar el espacio de nombres al archivo web.config.
Sin embargo, la pregunta le pregunta si "necesita" extender User.Identity para incluir la nueva propiedad. Hay una forma alternativa de acceder a la propiedad sin extender User.Identity. Si siguió el método Dhaust, puede usar el siguiente código en su controlador para acceder a la nueva propiedad.
ApplicationDbContext db = new ApplicationDbContext();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = manager.FindById(User.Identity.GetUserId());
var myNewProperty = currentUser.OrganizationId;
Echa un vistazo a esta gran publicación de blog de John Atten: ASP.NET Identity 2.0: Personalización de usuarios y roles
Tiene gran información paso a paso sobre todo el proceso. Ve a leerlo :)
Estos son algunos de los conceptos básicos.
Extienda la clase predeterminada ApplicationUser agregando nuevas propiedades (es decir, Dirección, Ciudad, Estado, etc.):
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
// Use a sensible display name for views:
[Display(Name = "Postal Code")]
public string PostalCode { get; set; }
// Concatenate the address info for display in tables and such:
public string DisplayAddress
{
get
{
string dspAddress = string.IsNullOrWhiteSpace(this.Address) ? "" : this.Address;
string dspCity = string.IsNullOrWhiteSpace(this.City) ? "" : this.City;
string dspState = string.IsNullOrWhiteSpace(this.State) ? "" : this.State;
string dspPostalCode = string.IsNullOrWhiteSpace(this.PostalCode) ? "" : this.PostalCode;
return string.Format("{0} {1} {2} {3}", dspAddress, dspCity, dspState, dspPostalCode);
}
}
Luego agrega sus nuevas propiedades a su RegisterViewModel.
// Add the new address properties:
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
Luego actualice la Vista de registro para incluir las nuevas propiedades.
<div class="form-group">
@Html.LabelFor(m => m.Address, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Address, new { @class = "form-control" })
</div>
</div>
Luego actualice el método Register () en AccountController con las nuevas propiedades.
// Add the Address properties:
user.Address = model.Address;
user.City = model.City;
user.State = model.State;
user.PostalCode = model.PostalCode;
Estaba buscando la misma solución y Pawel me dio el 99% de la respuesta. Lo único que faltaba que necesitaba para que se mostrara la Extensión era agregar el siguiente Código Razor en la página cshtml (ver):
@using programname.Models.Extensions
Estaba buscando el Nombre, para mostrar en la esquina superior derecha de mi NavBar después de que el usuario inició sesión.
Pensé en publicar esto en caso de que ayude a alguien más, así que aquí está mi código:
Creé una nueva carpeta llamada Extensiones (bajo mi carpeta de modelos) y creé la nueva clase como Pawel especificó anteriormente:
IdentityExtensions.cs
using System.Security.Claims;
using System.Security.Principal;
namespace ProgramName.Models.Extensions
{
public static class IdentityExtensions
{
public static string GetUserFirstname(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
}
IdentityModels.cs
:
public class ApplicationUser : IdentityUser
{
//Extended Properties
public string FirstName { get; internal set; }
public string Surname { get; internal set; }
public bool isAuthorized { get; set; }
public bool isActive { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim("FirstName", this.FirstName));
return userIdentity;
}
}
Luego, en mi
_LoginPartial.cshtml
(en
Views/Shared
Carpetas
Views/Shared
) agregué
@using.ProgramName.Models.Extensions
Luego agregué el cambio a la siguiente línea de código que iba a usar el Nombre de usuario después de iniciar sesión:
@Html.ActionLink("Hello " + User.Identity.GetUserFirstname() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
Quizás esto ayude a alguien más en el futuro.
Para cualquiera que encuentre esta pregunta buscando cómo acceder a las propiedades personalizadas en ASP.NET Core 2.1, es mucho más fácil: tendrá un UserManager, por ejemplo, en _LoginPartial.cshtml, y luego simplemente puede hacerlo (suponiendo que "ScreenName" es un propiedad que ha agregado a su propio AppUser que hereda de IdentityUser):
@using Microsoft.AspNetCore.Identity
@using <namespaceWhereYouHaveYourAppUser>
@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager
@if (SignInManager.IsSignedIn(User)) {
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })"
method="post" id="logoutForm"
class="form-inline my-2 my-lg-0">
<ul class="nav navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">
Hello @((await UserManager.GetUserAsync(User)).ScreenName)!
<!-- Original code, shows Email-Address: @UserManager.GetUserName(User)! -->
</a>
</li>
<li class="nav-item">
<button type="submit" class="btn btn-link nav-item navbar-link nav-link">Logout</button>
</li>
</ul>
</form>
} else {
<ul class="navbar-nav ml-auto">
<li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Register">Register</a></li>
<li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Login">Login</a></li>
</ul>
}
Siempre que desee ampliar las propiedades de User.Identity con propiedades adicionales como la pregunta anterior, agregue estas propiedades a la clase ApplicationUser primero de la siguiente manera:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
// Your Extended Properties
public long? OrganizationId { get; set; }
}
Entonces, lo que necesita es crear un método de extensión como este (creo el mío en una nueva carpeta de Extensiones):
namespace App.Extensions
{
public static class IdentityExtensions
{
public static string GetOrganizationId(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("OrganizationId");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
}
Cuando cree la Identidad en la clase ApplicationUser, simplemente agregue el Reclamo -> OrganizationId así:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here => this.OrganizationId is a value stored in database against the user
userIdentity.AddClaim(new Claim("OrganizationId", this.OrganizationId.ToString()));
return userIdentity;
}
Una vez que haya agregado el reclamo y tenga su método de extensión, para que esté disponible como una propiedad en su Usuario. Identidad, agregue una declaración de uso en la página / archivo al que desea acceder :
en mi caso:
using App.Extensions;
dentro de un controlador y
@using. App.Extensions
@using. App.Extensions
dentro de un archivo de vista .cshtml.
EDITAR:
Lo que también puede hacer para evitar agregar una declaración de uso en cada Vista es ir a la carpeta Vistas y ubicar el archivo Web.config allí.
Ahora busque la etiqueta
<namespaces>
y agregue su espacio de nombres de extensión de esta manera:
<add namespace="App.Extensions" />
Guarde su archivo y listo. Ahora cada Vista sabrá de sus extensiones.
Puede acceder al Método de extensión:
var orgId = User.Identity.GetOrganizationId();
Espero que ayude a cualquiera :)
También agregué o extendí columnas adicionales en mi tabla AspNetUsers. Cuando solo quería ver estos datos, encontré muchos ejemplos como el código anterior con "Extensiones", etc. Esto realmente me sorprendió de que tuviera que escribir todas esas líneas de código solo para obtener un par de valores de los usuarios actuales.
Resulta que puede consultar la tabla AspNetUsers como cualquier otra tabla:
ApplicationDbContext db = new ApplicationDbContext();
var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();