Внедрение IPrincipal в ASP.NET MVC 3 — что я делаю неправильно?

У меня есть собственная реализация IIdentity:

public class FeedbkIdentity : IIdentity
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string Name { get; set; }

    public FeedbkIdentity() 
    {
        // Empty contructor for deserialization
    }

    public FeedbkIdentity(string name)
    {
        this.Name = name;
    }

    public string AuthenticationType
    {
        get { return "Custom"; }
    }

    public bool IsAuthenticated
    {
        get { return !string.IsNullOrEmpty(this.Name); }
    }
}

и пользовательский IPrincipal

public class FeedbkPrincipal : IPrincipal
{
    public IIdentity Identity { get; private set; }

    public FeedbkPrincipal(FeedbkIdentity customIdentity)
    {
        this.Identity = customIdentity;
    }

    public bool IsInRole(string role)
    {
        return true;
    }
}

В global.asax.cs я десериализую пользовательские данные FormsAuthenticationTicket и заменяю

HttpContext.Current.User:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

    if (authCookie != null)
    {
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        JavaScriptSerializer serializer = new JavaScriptSerializer();

        FeedbkIdentity identity = serializer.Deserialize<FeedbkIdentity>(authTicket.UserData);

        FeedbkPrincipal newUser = new FeedbkPrincipal(identity);
        HttpContext.Current.User = newUser;
    }
}

Затем в Razor Views я могу сделать:

@(((User as FeedbkPrincipal).Identity as FeedbkIdentity).FirstName) 

Я уже использую Ninject для внедрения пользовательского репозитория пользователей для поставщика членства, и я пытался связать IPrincipal с HttpContext.Current.User:

internal class NinjectBindings : NinjectModule
{
    public override void Load()
    {
        Bind<IUserRepository>().To<EFUserRepository>();
        Bind<IPrincipal>().ToMethod(ctx => ctx.Kernel.Get<RequestContext>().HttpContext.User).InRequestScope();
    }
}

но это не работает.

Все, что я пытаюсь сделать, это получить доступ к своим пользовательским свойствам IIdentity следующим образом: @User.Identity.FirstName

Как я могу заставить это работать?

ИЗМЕНИТЬ

Я ожидал, что путем привязки IPrincipal к HttpContext.Current.User, как предлагается здесь: inject-the-user-iprincipal">MVC3 + Ninject: Как правильно ввести IP-принцип пользователя? Я смогу получить доступ к @User.Identity.FirstName в своем приложении.

Если это не так, какова цель привязки IPrincipal к HttpContext.Current.User, как показано в связанном вопросе?


person LukeP    schedule 08.05.2012    source источник
comment
Определить не работает. Что не работает? Что оно делает? Чем это отличается от того, что вы ожидали? Есть ли ошибки? Вам нужно дать больше информации. Как бы вы отреагировали, если бы конечный пользователь пришел к вам и просто сказал, что это не работает? Обратите внимание, как все говорят Предполагая... это потому, что вы на самом деле не сказали нам, в чем проблема.   -  person Erik Funkenbusch    schedule 09.05.2012
comment
Я отредактировал сообщение, но я упомянул, что я хотел бы сделать чуть выше редактирования. Я хотел бы иметь возможность использовать @User.Identity.FirstName (который исходит из моего пользовательского IIdentity) в представлениях Razor.   -  person LukeP    schedule 09.05.2012
comment
вы должны привести его к своему пользовательскому типу IIdentity. Пользователь типизирован для базового интерфейса, поэтому для получения ваших пользовательских вещей требуется приведение.   -  person Erik Funkenbusch    schedule 09.05.2012
comment
@MystereMan Я понимаю. Но если это так, зачем кому-то привязывать IPrincipal к HttpContext.Current.User, как в связанном вопросе?   -  person LukeP    schedule 09.05.2012
comment
некоторые делают это для целей тестирования, но на самом деле это не обязательно с MVC, потому что MVC уже использует интерфейсы. По крайней мере, на мой взгляд, я не вижу веских причин для внедрения IPrincipal. Однако, если вы действительно хотите... вы должны убедиться, что ваш пользовательский IPricipal имеет собственный интерфейс (IFeedbkPrincipal) и вместо этого внедрить его.   -  person Erik Funkenbusch    schedule 09.05.2012
comment
@MystereMan Спасибо. Поэтому я предполагаю, что ответ на мой вопрос находится в одном из ответов ниже. Какой из них будет считаться лучшим?   -  person LukeP    schedule 09.05.2012
comment
А.Финклештейн - лучший способ.   -  person Erik Funkenbusch    schedule 09.05.2012


Ответы (2)


Я бы рекомендовал создать собственный базовый WebViewPage. См. http://haacked.com/archive/2011/02/21/change-base-type-of-a-razor-view.aspx

Тогда внутри, что вы могли бы сделать

public FeedbkPrincipal FeedbkPrincipal
{
    get
    {
        return User as FeedbkPrincipal;
    }
}

public FeedbkIdentity FeedbkIdentity
{
     get
     {
         return FeedbkPrincipal.Identity as FeedbkIdentity;
     }
}

Тогда это просто

@FeedbkIdentity.FirstName

внутри представления.

какова цель привязки IPrincipal к HttpContext.Current.User, как показано в связанном вопросе

Внедрение зависимостей позволяет выбирать компоненты во время выполнения. В приведенном примере вы можете сделать

public MyClassConstructor(IPrincipal principal) { // }

и IPrincipal будет автоматически привязан к пользователю. Обратите внимание, что это не позволит вам получить доступ к вашим конкретным свойствам FeedbkPrincipal, потому что, хотя во время выполнения он станет типом FeedbkPrincipal, ваш класс этого не знает. Внедрение зависимостей не является решением вашей текущей проблемы, потому что вам нужен способ доступа к вашим конкретным классам.

person DMulligan    schedule 09.05.2012
comment
Большое спасибо. Это позволит мне использовать мой класс Identity в представлениях. Если бы я хотел использовать их и в контроллерах, я мог бы создать базовый контроллер и заставить все контроллеры наследовать тип, как показано ниже: stackoverflow.com/questions/7044424/ . Смогу ли я получить доступ к этим пользовательским свойствам в представлениях, если я реализую их на уровне контроллера? - person LukeP; 09.05.2012
comment
В пользовательской WebViewPage у вас может быть свойство, которое приводит ваш контроллер к вашему базовому типу. Затем у вас есть второе свойство, которое MyBaseController.MyUser извлекает ваши пользовательские свойства. - person DMulligan; 09.05.2012
comment
Это не работает. Для начала мне нужно иметь 2 отдельные реализации для WebViewPage и WebViewPage‹T›. Затем, после того как я реализую ваш код, он все еще не распознает @FeedbkPrincipal в представлениях. - person LukeP; 10.05.2012
comment
Для WebViewPage вы можете просто иметь общедоступный абстрактный класс WebViewPage : WebViewPage‹dynamic› { }, а затем поместить всю логику в WebViewPage‹T›. Если вы правильно реализовали его в своем файле конфигурации, он должен работать. Если intellisense не улавливает его, попробуйте закрыть и снова открыть визуальные студии. - person DMulligan; 10.05.2012
comment
Спасибо. Это сработало. Нет ли способа переопределить объект @User в представлениях? Когда я пытаюсь это сделать, я попадаю в бесконечный цикл. - person LukeP; 10.05.2012
comment
Отличный материал. У меня все заработало. Задокументировано все это в другом ответе - stackoverflow.com/a/10524305/239819. Большое спасибо! - person LukeP; 10.05.2012

Предполагая, что HttpContext.User установлен правильно, вы можете создать глобальный фильтр для автоматического заполнения вашего ViewBag вашим FeedbkIdentity.

E.g.

Создать это

public class FeedbkPrincipalIntoViewBagAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var principal = filterContext.HttpContext.User as FeedbkPrincipal;
        if (principal != null)
        {
            filterContext.Controller.ViewBag.Identity = principal.Identity as FeedbkIdentity;
        }
    }
}

Зарегистрируйте его

protected void Application_Start()
{
    GlobalFilters.Filters.Add(new FeedbkPrincipalIntoViewBagAttribute());
}

Используй это

@ViewBag.Identity.FirstName
person Charlino    schedule 09.05.2012