How can I make accessing my custom IPrincipal easier in ASP.NET MVC?

前端 未结 6 1228
失恋的感觉
失恋的感觉 2021-02-14 23:18

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

相关标签:
6条回答
  • 2021-02-15 00:03

    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();
    }
    
    0 讨论(0)
  • 2021-02-15 00:10

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-15 00:12

    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...
        }
    }
    
    0 讨论(0)
  • 2021-02-15 00:17

    The best way to make your IPrincipal implementation accessible in your Razor pages using ASP.NET MVC, is doing the following:

    1. Implement the System.Security.Principal.IPrincipal interface.
    2. Implement the System.Security.Principal.IIdentity interface.
    3. In Global.asax define a method for: void Application_AuthenticateRequest(Object, EventArgs) that persists your both implementations of IPrincipal and IIdentity.
    4. Create an extension method for IPrincipal to expose your implementation of IIdentity.
    5. Finally, add the namespace for the previous extension method in your web.config file in <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

    0 讨论(0)
  • 2021-02-15 00:17

    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;
    }
    
    0 讨论(0)
  • 2021-02-15 00:19

    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"
        }
    }
    
    0 讨论(0)
提交回复
热议问题