Подтвердить что ты не робот

Как интегрировать "Пользователи" в мою модель DDD с аутентификацией пользователей?

Я создаю свой первый сайт ASP.NET MVC и стараюсь следить за развитием домена. Мой сайт - это сайт совместной работы по проекту, на котором пользователи могут быть привязаны к одному или нескольким проектам на сайте. Затем к проектам добавляются задачи, а пользователям с проектом можно назначить задачи. Таким образом, "Пользователь" является фундаментальной концепцией моей модели домена.

Мой план состоит в том, чтобы иметь объект модели "Пользователь", который содержит всю информацию о пользователе и может быть доступен через IUserRepository. Каждый пользователь может быть идентифицирован с помощью UserId. Хотя я не уверен в этом, если я хочу, чтобы UserId был строкой или целым числом.

Как мои объекты домена User и IUserRepository относятся к более административным функциям моего сайта, например, авторизации пользователей и разрешению им входа? Как интегрировать мою модель домена с другими аспектами ASP.NET, такими как HttpContext.User, HttpContext.Profile, пользовательский MemberShipProvider, настраиваемый ProfileProvider или пользовательский атрибут AuthorizeAttribute?

Должен ли я создать пользовательский MemberhipProvider и или ProfileProvider, который переносит мой IUserRepository? Хотя, я также могу предвидеть, почему я могу отделить информацию пользователя в моей модели домена от авторизации пользователя на моем сайте. Например, в будущем я могу переключиться на проверку подлинности Windows из проверки подлинности форм.

Было бы лучше не пытаться изобретать колесо и придерживаться стандартного SqlMembershipProvider, встроенного в ASP.NET? Каждая информация профиля пользователя будет храниться в модели домена (User/IUserRepository), но это не будет включать их пароль. Затем я бы использовал стандартный материал для членства в ASP.NET для обработки и авторизации пользователей? Поэтому там должен быть какой-то код, который знал бы, чтобы создать профиль для новых пользователей в IUserRepository, когда их учетная запись создана или когда они впервые вошли в систему.

4b9b3361

Ответ 1

Да - очень хороший вопрос. Как @Andrew Cooper, наша команда также прошла через все это.

Мы пошли со следующими подходами (правильно или неправильно):

Пользовательский поставщик членства

Ни я, ни другой разработчик не являются поклонниками встроенного провайдера членства ASP.NET. Это слишком раздуто для нашего сайта (простой, ориентированный на UGC социальный сайт). Мы создали очень простой, который делает то, что нужно нашему приложению, и не более того. Принимая во внимание, что встроенный поставщик членства делает все, что вам может понадобиться, но, скорее всего, не будет.

Пользовательская проверка подлинности/аутентификация форм

В нашем приложении используется инжекция зависимостей с интерфейсом (StructureMap). Это включает проверку подлинности с помощью форм. Мы создали очень тонкий интерфейс:

public interface IAuthenticationService
{
   void SignIn(User user, HttpResponseBase httpResponseBase);
   void SignOut();
}

Этот простой интерфейс позволяет легко насмехаться/тестировать. При реализации мы создаем пользовательский билет проверки подлинности, содержащий: такие вещи, как UserId и Роли, которые требуются для каждого HTTP-запроса, не часто меняются и поэтому не должны извлекаться при каждом запросе.

Затем мы используем фильтр действий для дешифрования билета проверки подлинности форм (включая роли) и вставляем его в HttpContext.Current.User.Identity (для которого наш объект Principal также основан на интерфейсе).

Использование [Авторизовать] и [AdminOnly]

Мы все еще можем использовать атрибуты авторизации в MVC. И мы также создали один для каждой роли. [AdminOnly] просто проверяет роль для текущего пользователя и выбрасывает 401 (запрещено).

Простая единая таблица для пользователя, простая POCO

Вся информация пользователя хранится в одной таблице (за исключением "необязательной" информации пользователя, например интересов профиля). Это сопоставляется с простой POCO (Entity Framework), которая также имеет логику домена, встроенную в объект.

Пользовательский репозиторий/служба

Простой репозиторий пользователей, для домена. Такие вещи, как смена пароля, обновление профиля, загрузка пользователей и т.д. Репозиторий вызывает логику домена в объекте User, упомянутом выше. Служба представляет собой тонкую оболочку поверх репозитория, которая разделяет отдельные методы репозитория (например, "Найти" ) на более специализированные (FindById, FindByNickname).

Домен, отделенный от безопасности

Наш "домен" Пользователь и его/ее информация об ассоциации. Это включает имя, профиль, facebook/социальную интеграцию и т.д.

Такие вещи, как "Вход", "Выход", имеют аутентификацию, и такие вещи, как "User.IsInRole", связаны с авторизацией и, следовательно, не принадлежат к домену.

Итак, наши контроллеры работают как с IAuthenticationService, так и с IUserService.

Создание профиля - прекрасный пример логики домена, который также смешан с логикой аутентификации.

Вот что выглядит:

[HttpPost]
[ActionName("Signup")]
public ActionResult Signup(SignupViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            // Map to Domain Model.
            var user = Mapper.Map<SignupViewModel, Core.Entities.Users.User>(model);

            // Create salt and hash password.           
            user.Password = _authenticationService.SaltAndHashPassword();

            // Signup User.
            _userService.Save(user);

            // Save Changes.
            _unitOfWork.Commit();

            // Forms Authenticate this user.
            _authenticationService.SignIn(user, Response);

            // Redirect to homepage.
            return RedirectToAction("Index", "Home", new { area = "" });
        }
        catch (Exception exception)
        {
            ModelState.AddModelError("SignupError", "Sorry, an error occured during Signup. Please try again later.");
            _loggingService.Error(exception);
        }
    }

    return View(model);
}

Резюме

Вышеупомянутое хорошо сработало для нас. Мне нравится иметь простой пользовательский стол, а не то раздутое безумие, которое является поставщиком членства ASP.NET. Он прост и представляет наш домен, а не его представление ASP.NET.

Говоря, как я уже сказал, у нас есть простой сайт. Если вы работаете на банковском веб-сайте, я буду осторожен в том, чтобы повторно изобрести колесо.

Мой совет - сначала создать свой домен/модель, прежде чем вы даже подумаете об аутентификации. (конечно, именно это и есть DDD).

Затем выполните требования безопасности и выберите поставщика проверки подлинности (вне полки или пользовательского).

Не позволяйте ASP.NET определять, как должен быть разработан ваш домен. Это ловушка, в которую попадают большинство людей (включая меня, по предыдущему проекту).

Удачи!

Ответ 2

Позвольте мне немного сломать вашу коллекцию вопросов:

Хотя я не уверен в этом, если я хочу, чтобы UserId был строкой или целым числом.

Это не обязательно должно быть целым числом, но определенно использовать здесь какое-то битовую ценность (например, int, long или guid). Индекс, работающий над фиксированным размером, намного быстрее, чем индекс над строкой, и в течение вашего жизненного цикла у вас никогда не будет недостатков в идентификаторах для ваших пользователей.

Как мои объекты домена User и IUserRepository относятся к более административным функциям моего сайта, например, авторизации пользователей и разрешению им входа?

Решите, хотите ли вы использовать встроенное членство asp.net или нет. Я рекомендую не по той причине, что это в основном просто раздувается, и вы должны реализовать большинство его функций в любом случае, например, проверку электронной почты, что, по вашему мнению, будет выглядеть на основе таблиц, которые будут построены... Шаблон проект для ASP.NET MVC 1 и 2 включает простой репозиторий членства, просто перепишите функции, которые фактически проверяют пользователя, и вы будете хорошо на своем пути.

Как мне интегрировать мою модель домена с другими аспектами ASP.NET, такими как HttpContext.User, HttpContext.Profile, пользовательский MemberShipProvider, пользовательский ProfileProvider или пользовательский атрибут AuthorizeAttribute?

Каждый из них достоин своего собственного SO-вопроса, и каждый из них был задан здесь раньше. При этом HttpContext.User полезен только в том случае, если вы используете встроенную функцию FormsAuthentication, и я рекомендую использовать ее в начале, пока вы не столкнетесь с ситуацией, когда она не делает то, что вы хотите. Мне нравится хранить ключ пользователя в имени при входе с FormsAuthentication и загрузке запроса, связанного с текущим пользовательским объектом, в начале каждого запроса, если HttpContext.User.IsAuthenticated - true.

Что касается профиля, я избегаю запросов, связанных с состоянием, со страстью и никогда не использовал его раньше, поэтому кто-то еще должен будет помочь вам с этим.

Все, что вам нужно для использования встроенного атрибута [Authorize], - это сообщить FormsAuthentication, что пользователь валидирован. Если вы хотите использовать функцию ролей атрибута authorize, напишите свой собственный RoleProvider, и он будет работать как магия. Вы можете найти множество примеров для этого в Stack Overflow. HACK: вам нужно реализовать RoleProvider.GetAllRoles(), RoleProvider.GetRolesForUser(string username) и RoleProvider.IsUserInRole(string username, string roleName), чтобы он работал. Вам не нужно реализовывать весь интерфейс, если вы не хотите использовать все функции системы членства asp.net.

Было бы лучше не пытаться изобретать колесо и придерживаться стандартного SqlMembershipProvider, встроенного в ASP.NET?

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

if (built in stuff works fine) {
    use the built in stuff;  
} else {
    write your own;
}

if (easier to write your own then figure out how to use another tool) {
    write your own;
} else {
    use another tool;
}

if (need feature not in the system) {
    if (time to extend existing api < time to do it yourself) {
        extend api;
    } else {
        do it yourself;
    }
}

Ответ 3

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

Ниже приведен пример пользовательской проверки подлинности и авторизации с использованием ролей. http://www.codeproject.com/Articles/408306/Understanding-and-Implementing-ASP-NET-Custom-Form. Это очень хорошая статья, очень свежая и недавняя.

По-моему, вы должны реализовать эту реализацию как часть инфраструктуры (просто создайте новый проект Security или все, что вы хотите назвать) и выполните этот пример выше. Затем вызовите этот механизм с вашего уровня приложения. Помните, что уровень приложения управляет и управляет всей операцией в вашем приложении. Уровень домена должен быть связан исключительно с бизнес-операциями, а не с доступом или постоянством данных и т.д. Он не знает, как вы аутентифицируете людей в своей системе.

Подумайте о кирпичной и минометной компании. Внедренная система доступа к отпечаткам пальцев не имеет ничего общего с этой операцией компании, но все же она является частью инфраструктуры (здания). Фактически, он контролирует, кто имеет доступ к компании, поэтому они могут выполнять свои обязанности. У вас нет двух сотрудников, один из которых сканирует свой отпечаток пальца, чтобы другой мог ходить и выполнять свою работу. У вас есть только служащий с указательным пальцем. Для "доступа" все, что вам нужно, это его палец... Итак, ваш репозиторий, если вы собираетесь использовать тот же UserRepository для аутентификации, должен содержать метод аутентификации. Если вы решили вместо этого использовать AccessService (это служба приложения, а не домен), вам необходимо включить UserRepository, чтобы вы могли получить доступ к этим пользовательским данным, получить информацию о его пальцах (имя пользователя и пароль) и сравнить его с тем, что исходит от форма (сканирование пальцев). Я правильно объяснил?

Большинство ситуаций DDD применимы к реальным жизненным ситуациям... когда дело доходит до архитектуры программного обеспечения.