Как я могу облегчить доступ к своему пользовательскому IPrincipal в ASP.NET MVC?

Я написал собственный основной объект, который содержит несколько дополнительных полей (адрес электронной почты и идентификатор пользователя в дополнение к имени пользователя).

Чтобы получить доступ к этим свойствам, я должен привести объект Context.User в качестве своего пользовательского участника.

@Html.GetGravitarImage((User as CustomPrincipal).Email)

Этот пользовательский принципал создается / десериализуется через Application_AuthenticateRequest в моем global.ascx. Вы можете увидеть этот вопрос, который я задал здесь для получения дополнительной информации.

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);
}

Однако, если пользователь не аутентифицирован, то приведение к CustomPrincipal завершится неудачно (потому что он не будет введен в методе, описанном выше), а результат (User as CustomPrincipal) вернет ноль, таким образом, давая мне нулевую ссылку исключение, когда мой метод выше пытается получить электронную почту.

Каково было бы чистое решение этой проблемы? Я хочу сделать доступ к своему пользовательскому принципалу легким, а выполнение следующих действий кажется громоздким:

@Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")

Это единственный способ справиться с этой ситуацией?

6 ответов

Я быстро что-то взбил. Один из возможных способов легко ввести пользовательский IPrincipal в ASP.NET MVC заключается в следующем:

1) Создайте своего собственного потомка интерфейса IPrincipal.

public interface IMyPrincipal : IPrincipal
{
    Guid UserId { get; }
    string EmailAddress { get; }
}

2) Давайте предположим, что вы используете провайдера ASP.NET для проверки подлинности ваших пользователей. Давайте быстро создадим реализацию IMyPrincipal, которая использует 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) Создайте свой собственный тип базового класса для ваших контроллеров. Скройте унаследованный член User и представьте своего собственного 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) Зарегистрируйте фабрику контроллера в обработчике событий Application_Start() Global.asax.

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", либо создать метод расширения, подобный этому:

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...
    }
}

Лучший способ сделать ваш IPrincipal Реализация, доступная на ваших страницах Razor с использованием ASP.NET MVC, делает следующее:

  1. Реализовать System.Security.Principal.IPrincipal интерфейс.
  2. Реализовать System.Security.Principal.IIdentity интерфейс.
  3. В Global.asax определить метод для: void Application_AuthenticateRequest(Object, EventArgs) что сохраняется ваши обе реализации IPrincipal а также IIdentity,
  4. Создать метод расширения для IPrincipal разоблачить вашу реализацию IIdentity,
  5. Наконец, добавьте пространство имен для предыдущего метода расширения в файл web.config в <system.web.webPages.razor>,

В конце вы сможете получить доступ к своей пользовательской реализации IIdentity вместо литья типа. Теперь вы можете получить доступ к своей пользовательской реализации следующим образом:

Hello @User.CustomIdentity().FirstName @User.CustomerIdentity().LastName!

Эти шаги представляют собой краткое и подробное описание хорошо подробной статьи, написанной здесь: http://rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html

Вы также можете создать методы расширения для 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;
}

Вы можете создать какой-либо служебный метод или добавить метод к одному из ваших сервисов, который проверяет, является ли он вашим пользовательским принципалом. Может быть что-то вроде:

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;
        }
    }
}
Другие вопросы по тегам