如何在ASP.NET MVC中更轻松地访问我的自定义IPrincipal?
我编写了一个自定义主体对象,其中包含一些额外的字段(除了用户名之外的电子邮件和用户ID)。
为了访问这些属性,我必须将Context.User对象转换为我的自定义主体。
@Html.GetGravitarImage((User as CustomPrincipal).Email)
通过我的global.ascx中的Application_AuthenticateRequest创建/反序列化此自定义主体。 您可以在此处查看此问题以获取更多信息。
private void Application_AuthenticateRequest(Object source, EventArgs e) { var application = (HttpApplication)source; var context = application.Context; // Get the authentication cookie string cookieName = FormsAuthentication.FormsCookieName; HttpCookie authCookie = context.Request.Cookies[cookieName]; if (authCookie == null) return; var authTicket = FormsAuthentication.Decrypt(authCookie.Value); context.User = CustomPrincipal.CreatePrincipalFromCookieData(authTicket.UserData); }
但是,如果用户未经过身份validation,那么我对CustomPrincipal的强制转换将失败(因为它不会在上面的方法中注入),并且(User as CustomPrincipal)的结果将返回null,从而给我一个空引用上面我的方法尝试获取电子邮件时的exception。
什么是这个问题的干净解决方案? 我想让访问我的自定义主体变得容易,并且必须执行以下操作似乎很麻烦:
@Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")
这是处理这种情况的唯一方法吗?
我很快就把东西扯到了一起。 在ASP.NET MVC中轻松引入自定义IPrincipal的一种可能方法如下:
1)创建自己的IPrincipal接口后代。
public interface IMyPrincipal : IPrincipal { Guid UserId { get; } string EmailAddress { get; } }
2)假设您使用ASP.NET成员资格提供程序来validation您的用户。 让我们快速构建一个利用成员资格API的IMyPrincipal实现。
public class MyPrincipal : IMyPrincipal { private MembershipUser _user; public MyPrincipal() { this._user = Membership.GetUser(); var userName = this._user != null ? this._user.UserName : String.Empty; this.Identity = new GenericIdentity(userName); } public Guid UserId { get { return this._user != null ? (Guid) this._user.ProviderUserKey : default(Guid); } } public string EmailAddress { get { return this._user != null ? this._user.Email : null; } } public IIdentity Identity { get; private set; } public bool IsInRole(string role) { return false; } }
3)为控制器创建自己的基类类型。 隐藏inheritance的用户成员并介绍您自己的IPrincipal后代。
public class BaseController : Controller { protected virtual new MyPrincipal User { get { return HttpContext.User as MyPrincipal; } } }
4)让所有控制器都从这个新的BaseController类型下降。
public class HomeController : BaseController { //... }
5)创建自己的控制器工厂,以确保在HttpContext / Thread上引入了主体。
public class MyControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance (RequestContext requestContext, Type controllerType) { try { var controller = base.GetControllerInstance(requestContext, controllerType); requestContext.HttpContext.User = Thread.CurrentPrincipal = new MyPrincipal(); return controller; } catch (Exception) { return base.GetControllerInstance(requestContext, controllerType); } } }
6)在Global.asax的Application_Start()事件处理程序中注册控制器工厂。
var controllerFactory = new MyControllerFactory(); ControllerBuilder.Current.SetControllerFactory(controllerFactory);
瞧,现在您可以在控制器的任何位置使用新用户(IMyPrincipal)。
例如:
public ActionResult Index() { ViewBag.Message = "Welcome to ASP.NET MVC!"; ViewBag.UserId = User.UserId; ViewBag.UserName = User.EmailAddress; return View(); }
您可以使用“new”关键字创建基类并覆盖“User”属性,也可以创建如下的扩展方法:
public static class ControllerExtensions { public static CustomPrincipal CustomPrincipal(this Controller controller) { if(controller.User is CustomPrincipal) { return controller.User as CustomPrincipal; } return null; // maybe return an empty object instead to get around null reference... } }
使用ASP.NET MVC在Razor页面中访问IPrincipal
实现的最佳方法是执行以下操作:
- 实现
System.Security.Principal.IPrincipal
接口。 - 实现
System.Security.Principal.IIdentity
接口。 - 在
Global.asax
定义一个方法:void Application_AuthenticateRequest(Object, EventArgs)
,它持久保存IPrincipal
和IIdentity
两个实现。 - 为
IPrincipal
创建扩展方法以公开您的IIdentity
实现。 - 最后,在
web.config文件中添加先前扩展方法的命名空间。
最后,您将能够访问IIdentity
的自定义实现,而不是类型转换。 您现在可以像这样访问自定义实现 :
Hello @User.CustomIdentity().FirstName @User.CustomerIdentity().LastName!
这些步骤简明扼要地描述了这里写的详细文章: http : //rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html
您可以创建某种实用方法或向您的某个服务添加方法,以检查它是否是您的自定义主体。 也许是这样的:
public class UserService : IUserService { public CustomPrincipal CurrentUser { get { CustomPrincipal user = HttpContext.Current.User as CustomPrincipal; if (user == null) return GuestUser; // Just some default user object return user; } } }
您也可以使用与John Kalberer的答案相同的方式为Email和UserID制作扩展方法:
public static class CustomPrincipalExtensions { public static string Email(this CustomPrincipal cUser) { return cUser != null ? cUser.Email : "Default Email" } public static string UserID(this CustomPrincipal cUser) { return cUser != null ? cUser.UserID : "Default ID" } }
未经授权时,您可以使用默认值将用户对象设置为自定义主体的特定实例:
if (authCookie == null) { context.User = CustomPrincipal.Default; // Or CreateDefault() return; }