如何在MVC5中获取当前登录用户的特定字段?

tet*_*ett 5 c# asp.net-mvc

我有一个使用个人身份验证的MVC5应用程序,当然还有ASP.NET身份.关键是我扩展了我有一个继承自ApplicationUser的模型,它只是这样定义:

public class NormalUser : ApplicationUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

所以,重点是,首先我想检查是否有登录用户,如果有,我想获得他/她的FirstName,LastName和Email字段.我怎样才能实现它?

我想我需要使用这样的东西来检查是否有登录用户:

if (Request.IsAuthenticated)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

但是,如何为当前用户获取这些特定字段的值?

Sha*_*hay 9

在MVC5中,用户数据默认存储在会话中,并且根据请求将数据解析为ClaimsPrincipal包含用户名(或id)和声明的数据.

这是我选择实现它的方式,它可能不是最简单的解决方案,但它确实使它易于使用.

用法示例:

在控制器中:

public ActionResult Index()
{
    ViewBag.ReverseDisplayName = this.User.LastName + ", " + this.User.FirstName;
}
Run Code Online (Sandbox Code Playgroud)

在视图中或_Layout:

@if(User.IsAuthenticated)
{
     <span>@User.DisplayName</span>
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

5.注册auth过滤器以全局加载 FilterConfig

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute()); 
    filters.Add(new AppAuthenticationFilterAttribute());
}
Run Code Online (Sandbox Code Playgroud)

到现在Principal为止,我们还剩下要做的就是在Controller和View中公开它.

6.创建控制器基类

public abstract class ControllerBase : Controller
{
    public new UserPrincipal User
    {
        get { return HttpContext.User as UserPrincipal; }
    }
}
Run Code Online (Sandbox Code Playgroud)

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

和文件夹web.config里面Views:

<pages pageBaseType="MyApp.Web.Views.BaseViewPage">
Run Code Online (Sandbox Code Playgroud)

重要!

不要存储太多数据,Principal因为这些数据在每个请求上来回传递.

  • (不是你的错)这是MS的想法吗?善良. (2认同)

Chr*_*att 2

是的,在身份中,如果您需要其他用户信息,您只需从数据库中提取用户,因为这些信息现在都存储在实际的用户对象上。

if (Request.IsAuthenticated)
{
    var user = UserManager.FindById(User.Identity.GetUserId());
}
Run Code Online (Sandbox Code Playgroud)

如果GetUserId未开启User.Identity,请将以下内容添加到您的使用中:

using Microsoft.AspNet.Identity;
Run Code Online (Sandbox Code Playgroud)