Как я могу получить конкретные поля текущего зарегистрированного пользователя в MVC5?

У меня есть приложение MVC5, которое использует индивидуальную проверку подлинности и, конечно же, идентификатор ASP.NET. Дело в том, что я расширил модель, которая наследуется от ApplicationUser, она просто определяется следующим образом:

public class NormalUser : ApplicationUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Итак, дело в том, что, прежде всего, я хочу проверить, есть ли зарегистрированный пользователь, и если есть, я хочу получить его/ее поля FirstName, LastName и Email. Как я могу это достичь?

Я думаю, мне нужно использовать что-то вроде этого, чтобы проверить, есть ли зарегистрированный пользователь:

if (Request.IsAuthenticated)
{
    ...
}

Но как я могу получить значения этих конкретных полей для текущего пользователя?

Ответ 1

Да, в Identity, если вам нужна дополнительная информация о пользователе, вы просто вытаскиваете пользователя из базы данных, так как теперь все это сохраняется на фактическом объекте пользователя.

if (Request.IsAuthenticated)
{
    var user = UserManager.FindById(User.Identity.GetUserId());
}

Если GetUserId не находится на User.Identity, добавьте следующее к вашим сообщениям:

using Microsoft.AspNet.Identity;

Ответ 2

В MVC5 пользовательские данные сохраняются по умолчанию в сеансе, и по запросу данные анализируются в ClaimsPrincipal, который содержит имя пользователя (или идентификатор) и формулы.

Именно так я и решил реализовать его, это может быть не простое решение, но это определенно упрощает его использование.

Пример использования:

В контроллере:

public ActionResult Index()
{
    ViewBag.ReverseDisplayName = this.User.LastName + ", " + this.User.FirstName;
}

В поле зрения или _Layout:

@if(User.IsAuthenticated)
{
     <span>@User.DisplayName</span>
}

1. Заменить ClaimsIdentityFactory

using System.Security.Claims;
using System.Threading.Tasks;
using Domain.Models;
using Microsoft.AspNet.Identity;

public class AppClaimsIdentityFactory : IClaimsIdentityFactory<User, int>
{
    internal const string IdentityProviderClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";

    internal const string DefaultIdentityProviderClaimValue = "My Identity Provider";

    /// <summary>
    ///     Constructor
    /// </summary>
    public AppClaimsIdentityFactory()
    {
        RoleClaimType = ClaimsIdentity.DefaultRoleClaimType;
        UserIdClaimType = ClaimTypes.NameIdentifier;
        UserNameClaimType = ClaimsIdentity.DefaultNameClaimType;
        SecurityStampClaimType = Constants.DefaultSecurityStampClaimType;
    }

    /// <summary>
    ///     Claim type used for role claims
    /// </summary>
    public string RoleClaimType { get; set; }

    /// <summary>
    ///     Claim type used for the user name
    /// </summary>
    public string UserNameClaimType { get; set; }

    /// <summary>
    ///     Claim type used for the user id
    /// </summary>
    public string UserIdClaimType { get; set; }

    /// <summary>
    ///     Claim type used for the user security stamp
    /// </summary>
    public string SecurityStampClaimType { get; set; }

    /// <summary>
    ///     Create a ClaimsIdentity from a user
    /// </summary>
    /// <param name="manager"></param>
    /// <param name="user"></param>
    /// <param name="authenticationType"></param>
    /// <returns></returns>
    public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<User, int> manager, User user, string authenticationType)
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }
        var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
        id.AddClaim(new Claim(UserIdClaimType, user.Id.ToString(), ClaimValueTypes.String));
        id.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
        id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));

        id.AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
        if (user.ContactInfo.FirstName != null && user.ContactInfo.LastName != null)
        {
            id.AddClaim(new Claim(ClaimTypes.GivenName, user.ContactInfo.FirstName));
            id.AddClaim(new Claim(ClaimTypes.Surname, user.ContactInfo.LastName));
        }

        if (manager.SupportsUserSecurityStamp)
        {
            id.AddClaim(new Claim(SecurityStampClaimType,
                await manager.GetSecurityStampAsync(user.Id)));
        }
        if (manager.SupportsUserRole)
        {
            user.Roles.ToList().ForEach(r =>
                id.AddClaim(new Claim(ClaimTypes.Role, r.Id.ToString(), ClaimValueTypes.String)));
        }
        if (manager.SupportsUserClaim)
        {
            id.AddClaims(await manager.GetClaimsAsync(user.Id));
        }
        return id;
    }

2. Измените UserManager, чтобы использовать его

public static UserManager<User,int> Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
    var manager = new UserManager<User,int>(new UserStore<User,int>(new ApplicationDbContext()))
    {
        ClaimsIdentityFactory = new AppClaimsIdentityFactory()
    };

    // more initialization here

    return manager;
}

3. Создайте новый пользовательский Principal

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;

public class UserPrincipal : ClaimsPrincipal
{
    public UserPrincipal(ClaimsPrincipal principal)
        : base(principal.Identities)
    {
    }

    public int UserId
    {
        get { return FindFirstValue<int>(ClaimTypes.NameIdentifier); }
    }

    public string UserName
    {
        get { return FindFirstValue<string>(ClaimsIdentity.DefaultNameClaimType); }
    }

    public string Email
    {
        get { return FindFirstValue<string>(ClaimTypes.Email); }
    }

    public string FirstName
    {
        get { return FindFirstValue<string>(ClaimTypes.GivenName); }
    }

    public string LastName
    {
        get { return FindFirstValue<string>(ClaimTypes.Surname); }
    }

    public string DisplayName
    {
        get
        {
            var name = string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
            return name.Length > 0 ? name : this.UserName;
        }
    }

    public IEnumerable<int> Roles
    {
        get { return FindValues<int>(ClaimTypes.Role); }
    }

    private T FindFirstValue<T>(string type)
    {
        return Claims
            .Where(p => p.Type == type)
            .Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
            .FirstOrDefault();
    }

    private IEnumerable<T> FindValues<T>(string type)
    {
        return Claims
            .Where(p => p.Type == type)
            .Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
            .ToList();
    }
}

4. Создайте AuthenticationFilter, чтобы использовать его

using System.Security.Claims;
using System.Web.Mvc;
using System.Web.Mvc.Filters;


public class AppAuthenticationFilterAttribute : ActionFilterAttribute, IAuthenticationFilter
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {
        //This method is responsible for setting and modifying the principle for the current request though the filterContext .
        //Here you can modify the principle or applying some authentication logic.  
        var principal = filterContext.Principal as ClaimsPrincipal;
        if (principal != null && !(principal is UserPrincipal))
        {
            filterContext.Principal = new UserPrincipal(principal);
        }
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        //This method is responsible for validating the current principal and permitting the execution of the current action/request.
        //Here you should validate if the current principle is valid / permitted to invoke the current action. (However I would place this logic to an authorization filter)
        //filterContext.Result = new RedirectToRouteResult("CustomErrorPage",null);
    }
}

5. Зарегистрируйте фильтр авторизации для загрузки по всему миру в FilterConfig

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute()); 
    filters.Add(new AppAuthenticationFilterAttribute());
}

Теперь Principal сохраняется, и все, что нам осталось сделать, это разоблачить его в контроллере и представлении.

6. Создание базового класса контроллера

public abstract class ControllerBase : Controller
{
    public new UserPrincipal User
    {
        get { return HttpContext.User as UserPrincipal; }
    }
}

7. Создайте базовый класс WebViewPage и измените web.config, чтобы использовать его

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

    public bool IsAuthenticated
    {
        get { return base.User.Identity.IsAuthenticated; }
    }
}

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

    public bool IsAuthenticated
    {
        get { return base.User.Identity.IsAuthenticated; }
    }
}

И web.config внутри папки Views:

<pages pageBaseType="MyApp.Web.Views.BaseViewPage">

Внимание!

Не храните слишком много данных на Principal, так как эти данные передаются взад и вперед по каждому запросу.