Пользовательский принципал в ASP.NET MVC - PullRequest
3 голосов
/ 21 сентября 2010

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

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

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

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

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

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

AmЯ иду об этом правильным путем?Могу ли я сделать небольшие изменения, чтобы сделать его надежным решением, или я должен начать с нуля с FormsAuthenticationTicket или что-то в этом роде?

Спасибо!

Ответы [ 2 ]

7 голосов
/ 22 сентября 2010

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

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

В моем случае я изменил свою подпрограмму LogOn (которую я уже издевался в своих юнит-тестах), чтобы создать 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, ...

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

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

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

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

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

2 голосов
/ 21 сентября 2010

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

public interface ICustomPrincipalManager 
{
    ICustomPrincipal Current {get;}
}

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

_customPrincipalManager.Current.FirstName
...