Пользовательский принципал в ASP.NET MVC

Я хочу иметь доступ к пользовательским свойствам для аутентифицированного пользователя, такого как UserId и FirstName, без необходимости каждый раз запрашивать базу данных. Я нашел этот сайт через сообщение на Stack Overflow, и мне нравится подход - но я использую IoC / репозитории и решил не пытаться заставить global.asax общаться с базой данных, опасаясь, что это будет несовместимо с шаблоном репозитория.

Вместо этого я создал интерфейс для CustomPrincipal и использую IoC (Castle) для создания экземпляра и передачи его контроллерам (а затем и моему базовому контроллеру).

Базовый контроллер использует методы, которые я создал в CustomPrincipal, для решения той же задачи, которую автор блога решил в global.asax. А именно, CustomPrincipal инициализируется из базы данных или кэша и назначается HttpContext.Current.User.

Затем мои контроллеры/представления могут ссылаться на свойства следующим образом...

((ICustomPrincipal)(HttpContext.Current.User)).FirstName;

Это работает, но я чувствую некоторые запахи кода. Прежде всего, если я ссылаюсь на HttpContext из контроллеров, я убиваю свое модульное тестирование. Я думаю об изменении моего объекта CustomPrincipal, чтобы он возвращал указанное выше значение (чтобы я мог издеваться над ним в своих модульных тестах), но мне интересно, является ли это обходным путем, а не хорошим решением.

Правильно ли я поступаю? Есть ли небольшие изменения, которые я мог бы сделать, чтобы сделать его надежным решением, или мне следует начать с нуля с помощью FormsAuthenticationTicket или чего-то в этом роде?

Спасибо!


person Mayo    schedule 21.09.2010    source источник


Ответы (2)


Я хотел предложить альтернативную идею, чтобы у людей, которые ищут эту информацию, был выбор.

Я искал жизнеспособный пример FormsAuthenticationTicket и обнаружил, что NerdDinner неплохо справляется с добавлением пользовательских свойств, не влияя на модульное тестирование. .

В моем случае я изменил свою процедуру входа в систему (которую я уже издевался в своих модульных тестах), чтобы создать FormsAuthenticationTicket. NerdDinner шифрует билет и добавляет его как файл cookie, но я также могу добавить зашифрованный билет в кеш, как в исходное предложение. Я также заменил единственное свойство UserData сериализованным объектом JSON, представляющим все мои пользовательские свойства.

CustomIdentityDTO dto = new CustomIdentityDTO { 
   UserId = userId, FirstName = firstName, LastName = lastName };
JavaScriptSerializer serializer = new JavaScriptSerializer();

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
  1, // version
  username,
  DateTime.Now, // creation
  DateTime.Now.AddMinutes(30), // expiration
  false, // not persistent
  serializer.Serialize(dto));

string encTicket = FormsAuthentication.Encrypt(authTicket);
//HttpContext.Current.Response.Cookies.Add(...)
HttpContext.Current.Cache.Add(username, encTicket, ...

Затем я получаю зашифрованный билет (либо из кэша, либо из файлов cookie) в файле global.asax через обработчик PostAuthenticateRequest, очень похожий на NerdDinner (для файлов cookie) или предложение блоггера (для кэша).

NerdDinner реализует IIdentity вместо IPrincipal. Ссылки на настраиваемые поля в коде следующие:

((CustomIdentity)Page.User.Identity).FirstName // from partial view

((CustomIdentity)User.Identity).FirstName // from controller

Поработав с обоими методами, я обнаружил, что подход NerdDinner работает очень хорошо. После переключения я не встретил много препятствий на пути.

person Mayo    schedule 22.09.2010

Как насчет создания интерфейса ICustomPrincipalManager?

public interface ICustomPrincipalManager 
{
    ICustomPrincipal Current {get;}
}

Его можно реализовать с помощью класса, который обращается к HttpContext, базе данных, кэшированию или чему-то еще, но вы также можете смоделировать интерфейс для модульного тестирования. Ваши контроллеры будут использовать инфраструктуру IoC для получения вашего ICustomPrincipalManager, а затем получать доступ к такой информации:

_customPrincipalManager.Current.FirstName
person StriplingWarrior    schedule 21.09.2010
comment
Это звучит как отличная идея — я собираюсь поиграть с этим, чтобы увидеть, смогу ли я заставить его работать. Спасибо! - person Mayo; 21.09.2010
comment
Без проблем. Дайте мне знать, если вы столкнетесь с какими-либо препятствиями. - person StriplingWarrior; 21.09.2010