I\'ve written a custom principal object which contains a few additional fields (email and userid in addition to the username).
In order to access these properties I have
I whipped something together quickly. One possible way of easily introducing a custom IPrincipal in ASP.NET MVC is the following:
1) Create your own descendant of the IPrincipal interface.
public interface IMyPrincipal : IPrincipal
{
Guid UserId { get; }
string EmailAddress { get; }
}
2) Let's assume you are using the ASP.NET Membership provider to authenticate your users. Let's quickly build an IMyPrincipal implementation which utilizes the membership API.
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) Create your own base class type for your controllers. Hide the inherited User member and introduce your own IPrincipal descendant.
public class BaseController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
4) Have all your controllers descend from this new BaseController type.
public class HomeController : BaseController
{
//...
}
5) Create your own controller factory to make sure your principal is introduced on the 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) Register the controller factory in the Global.asax's Application_Start() event handler.
var controllerFactory = new MyControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
Voila, now you can use the new User (IMyPrincipal) anywhere in your controllers.
For example:
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.UserId = User.UserId;
ViewBag.UserName = User.EmailAddress;
return View();
}
You could create some sort of utility method or add a method to one of your services that checks if it's your custom principal. Maybe something like:
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;
}
}
}
You could either create a base class and override the "User" property using the "new" keyword or create an extension method like this:
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...
}
}
The best way to make your IPrincipal
implementation accessible in your Razor pages using ASP.NET MVC, is doing the following:
System.Security.Principal.IPrincipal
interface.System.Security.Principal.IIdentity
interface.Global.asax
define a method for: void Application_AuthenticateRequest(Object, EventArgs)
that persists your both implementations of IPrincipal
and IIdentity
.IPrincipal
to expose your implementation of IIdentity
.<system.web.webPages.razor>
.At the end, you will be able to access your custom implementation of IIdentity
instead of type casting. You now can access your custom implementation like this:
Hello @User.CustomIdentity().FirstName @User.CustomerIdentity().LastName!
These steps are a concise and brief description of a well detailed article written here: http://rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html
When not authorized, you could set the user object to a specific instance of the custom principal with default values:
if (authCookie == null)
{
context.User = CustomPrincipal.Default; // Or CreateDefault()
return;
}
You could also make extension methods for the Email and UserID in the same fashion as John Kalberer's answer:
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"
}
}