使用MVC5和OWIN的自定义标识

我尝试使用MVC5和OWIN身份validation为Web站点的ApplicationUser添加自定义属性。 我已经阅读了https://stackoverflow.com/a/10524305/264607 ,我喜欢它与基本控制器的集成方式,以便于访问新属性。 我的问题是,当我将HTTPContext.Current.User属性设置为我的新IPrincipal时,我得到一个空引用错误:

[NullReferenceException: Object reference not set to an instance of an object.] System.Web.Security.UrlAuthorizationModule.OnEnter(Object source, EventArgs eventArgs) +127 System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69 

这是我的代码:

  protected void Application_PostAuthenticateRequest(Object sender, EventArgs e) { if (HttpContext.Current.User.Identity.IsAuthenticated) { userManager = new UserManager(new UserStore(new ApplicationDbContext())); ApplicationUser user = userManager.FindByName(HttpContext.Current.User.Identity.Name); PatientPortalPrincipal newUser = new PatientPortalPrincipal(); newUser.BirthDate = user.BirthDate; newUser.InvitationCode = user.InvitationCode; newUser.PatientNumber = user.PatientNumber; //Claim cPatient = new Claim(typeof(PatientPortalPrincipal).ToString(), ); HttpContext.Current.User = newUser; } } public class PatientPortalPrincipal : ClaimsPrincipal, IPatientPortalPrincipal { public PatientPortalPrincipal(ApplicationUser user) { Identity = new GenericIdentity(user.UserName); BirthDate = user.BirthDate; InvitationCode = user.InvitationCode; } public PatientPortalPrincipal() { } public new bool IsInRole(string role) { if(!string.IsNullOrWhiteSpace(role)) return Role.ToString().Equals(role); return false; } public new IIdentity Identity { get; private set; } public WindowsBuiltInRole Role { get; set; } public DateTime BirthDate { get; set; } public string InvitationCode { get; set; } public string PatientNumber { get; set; } } public interface IPatientPortalPrincipal : IPrincipal { WindowsBuiltInRole Role { get; set; } DateTime BirthDate { get; set; } string InvitationCode { get; set; } string PatientNumber { get; set; } } 

我没有找到关于如何做到这一点的文档的方式,我读过这些文章:

http://blogs.msdn.com/b/webdev/archive/2013/10/16/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates.aspx

http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx

第二个链接中的注释指向我可能使用声明( http://msdn.microsoft.com/en-us/library/ms734687.aspx?cs-save-lang=1&cs-lang=csharp ),但文章链接to没有说明如何将这些添加到IPrincipal (这是HttpContext.Current.User是什么),或者在管道中你应该将它们添加到ClaimsIdentity (这是User的具体类)。 我倾向于使用声明,但我需要知道将这些新声明添加到用户的位置。

即使声称是要走的路,我也很好奇我的自定义IPrincipal出了什么问题,因为我似乎已经实现了它所需要的一切。

我可以使用基于Claims的安全性来获得一些工作,所以如果你想快速完成某些事情,我现在所拥有的是:

AccountController的登录过程中(我的是SignInAsync方法),向UserManager创建的标识添加新的声明:

 private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); identity.AddClaim(new Claim("PatientNumber", user.PatientNumber)); //This is what I added AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } 

然后在我的基本控制器类中,我只添加了一个属性:

 private string _patientNumber; public string PatientNumber { get { if (string.IsNullOrWhiteSpace(_patientNumber)) { try { var cp = ClaimsPrincipal.Current.Identities.First(); var patientNumber = cp.Claims.First(c => c.Type == "PatientNumber").Value; _patientNumber = patientNumber; } catch (Exception) { } } return _patientNumber; } } 

此链接有助于索赔知识: http : //msdn.microsoft.com/en-us/library/ms734687.aspx?cs-save-lang = 1&cs-lang = csharp#code -snippet-1


更新IPrincipal的问题

我将其跟踪到Identity属性。 问题是我在PatientPortalPrincipal类上提供了一个未设置Identity属性的默认构造函数。 我最终做的是删除默认构造函数并从Application_PostAuthenticateRequest调用正确的构造函数,更新的代码如下

 protected void Application_PostAuthenticateRequest(Object sender, EventArgs e) { if (HttpContext.Current.User.Identity.IsAuthenticated) { userManager = new UserManager(new UserStore(new ApplicationDbContext())); ApplicationUser user = userManager.FindByName(HttpContext.Current.User.Identity.Name); PatientPortalPrincipal newUser = new PatientPortalPrincipal(user); newUser.BirthDate = user.BirthDate; newUser.InvitationCode = user.InvitationCode; newUser.PatientNumber = user.PatientNumber; //Claim cPatient = new Claim(typeof(PatientPortalPrincipal).ToString(), ); HttpContext.Current.User = newUser; } } 

这使整个事情发挥作用!

你得到一个例外,因为HttpContext.Current.User.Identity.IsAuthenticated在检查点返回false( HttpContext.Current.Request.IsAuthenticated也是如此)。

如果删除if (HttpContext.Current.User.Identity.IsAuthenticated)语句,它将正常工作(至少这部分代码)。

我尝试过这样一个简单的事情:

BaseController.cs

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

CustomPrincipal.cs

 public class CustomPrincipal : IPrincipal { public IIdentity Identity { get; private set; } public bool IsInRole(string role) { return false; } public CustomPrincipal(string username) { this.Identity = new GenericIdentity(username); } public DateTime BirthDate { get; set; } public string InvitationCode { get; set; } public int PatientNumber { get; set; } } 

的Global.asax.cs

 protected void Application_PostAuthenticateRequest(Object sender, EventArgs e) { CustomPrincipal customUser = new CustomPrincipal(User.Identity.Name); customUser.BirthDate = DateTime.Now; customUser.InvitationCode = "1234567890A"; customUser.PatientNumber = 100; HttpContext.Current.User = customUser; } 

HomeController.cs

 public ActionResult Index() { ViewBag.BirthDate = User.BirthDate; ViewBag.InvitationCode = User.InvitationCode; ViewBag.PatientNumber = User.PatientNumber; return View(); } 

这工作正常。 所以,除非这段代码:

 userManager = new UserManager(new UserStore(new ApplicationDbContext())); ApplicationUser user = userManager.FindByName(HttpContext.Current.User.Identity.Name); 

没有返回有效(自定义)用户对象,问题在于if()语句。

您的更新看起来很好,如果您乐意将数据存储为cookie中的声明,您可以使用它,尽管我个人讨厌那里的try {} catch块。

我做的是这样的:

BaseController.cs

 [AuthorizeEx] public abstract partial class BaseController : Controller { public IOwinContext OwinContext { get { return HttpContext.GetOwinContext(); } } public new ClaimsPrincipal User { get { return base.User as ClaimsPrincipal; } } public WorkContext WorkContext { get; set; } } 

我用自定义属性装饰基本控制器类。

AuthorizeExAttribute.cs:

 public class AuthorizeExAttribute : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { Ensure.Argument.NotNull(filterContext); base.OnAuthorization(filterContext); IPrincipal user = filterContext.HttpContext.User; if (user.Identity.IsAuthenticated) { var ctrl = filterContext.Controller as BaseController; ctrl.WorkContext = new WorkContext(user.Identity.Name); } } } 

和WorkContext.cs:

 public class WorkContext { private string _email; private Lazy currentUser; private IAuthenticationService authService; private ICacheManager cacheManager; public User CurrentUser { get { var cachedUser = cacheManager.Get(Constants.CacheUserKeyPrefix + this._email); if (cachedUser != null) { return cachedUser; } else { var user = currentUser.Value; cacheManager.Set(Constants.CacheUserKeyPrefix + this._email, user, 30); return user; } } } public WorkContext(string email) { Ensure.Argument.NotNullOrEmpty(email); this._email = email; this.authService = DependencyResolver.Current.GetService(); this.cacheManager = DependencyResolver.Current.GetService(); this.currentUser = new Lazy(() => authService.GetUserByEmail(email)); } 

然后我像这样访问WorkContext:

 public class DashboardController : BaseController { public ActionResult Index() { ViewBag.User = WorkContext.CurrentUser; return View(); } } 

我正在使用Ninject的Dependency Resolver来解析authServicecacheManager但我相信你可以跳过缓存并用ASP.NET Identity UserManager替换authService。

由于WorkContext课程受NugetGallery项目的启发,我还想给予应有的信任。

我敢打赌HttpContext.Current.User为null。 所以不是这样的:

 if (HttpContext.Current.User.Identity.IsAuthenticated) 

你可以试试这个:

 if (HttpContext.Current.Request.IsAuthenticated) 

我有同样的错误。

我的问题是,对于匿名用户,我没有在IPrincipal上设置IIdentity。 我只在用户使用用户名登录时才这样做。 否则,IIdentity为空。

我的解决方案是始终设置IIdentity。 如果用户未经过身份validation(匿名用户),则IIdentity.IsAuthenticated将设置为false。 否则,是的。

我的代码:

 private PrincipalCustom SetPrincipalIPAndBrowser() { return new PrincipalCustom { IP = RequestHelper.GetIPFromCurrentRequest(HttpContext.Current.Request), Browser = RequestHelper.GetBrowserFromCurrentRequest(HttpContext.Current.Request), /* User is not authenticated, but Identity must be set anyway. If not, error occurs */ Identity = new IdentityCustom { IsAuthenticated = false } }; }