Многопользовательское приложение ASP.NET с ролями, зависящими от клиента

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

Соответствующие технические детали:

  • AspNetSqlMembershipProvider как для членства, так и для ролей
  • C # 4.0 (скоро будет 4.5)
  • Формы аутентификации
  • Страницы как aspx, так и MVC (v3)
  • Предположим, что 100 или более арендаторов, поэтому любое решение должно поддерживать это

Я считаю, что требования очень похожи на модель безопасности для SQL Server. У нас есть один набор логинов, которые представляют всех пользователей, которые могут войти в систему. Пользователи должны иметь возможность назначать роли для одной или нескольких баз данных (клиентов). Пример: пользователь Боб имеет роль администратора в компании A, но только роль пользователя в компании B. У нас также есть роль «системного администратора» для сотрудников моей компании, которая позволяет нам получить доступ к любому арендатору, а также к специализированным административным привилегиям, таким как создание / удаление клиентов. , и т.д.

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

У меня есть 2 варианта, которые я рассматриваю:

  1. Добавьте к провайдеру членства лишь несколько ролей и обработайте все вопросы «имеет ли текущий пользователь эту роль в этом клиенте» вне провайдера членства. Поставщик членства будет использоваться для управления базовым доступом к системе.
  2. Добавьте к поставщику членства роли для конкретного клиента. У нас будет (# ролей) x (# арендаторов) общих ролей в системе. Каждый новый клиент будет добавлять в систему еще один набор ролей, например «Клиент A: Администратор», «Клиент A: Пользователь» и т. Д. Потребуются некоторые дополнительные таблицы для управления отношениями, а также, возможно, некоторый настраиваемый код, чтобы гарантировать, что доступ запрашивает правильную роль, зависящую от клиента, от поставщика членства.

Любой из этих вариантов хорош? Или мне следует искать поддержку в другом месте?


person Ted Elliott    schedule 20.01.2014    source источник
comment
Привет, Тед, я недавно ответил на аналогичный вопрос здесь, может быть, это полезно: stackoverflow.com/q/21165928/304832 В Короче, я думаю, что вариант №1 будет лучшим вариантом.   -  person danludwig    schedule 21.01.2014
comment
Похоже, что обработка конкретных ролей клиента за пределами поставщика членства и просто предоставление поставщику членства возможности грубого доступа - это путь.   -  person Ted Elliott    schedule 23.01.2014


Ответы (2)


Я не думаю, что вы сможете внедрить мультиарендность в какой-либо нестандартный поставщик ролей, поэтому вы можете продолжать использовать SqlMembershipProvider (и SqlRoleProvider). Даже новейшая версия Microsoft.AspNet.Identity по-прежнему предполагает ванильный принцип «многие ко многим» между пользователями и ролями. Что вам действительно нужно, так это добавить третий столбец к первичному ключу этой таблицы многие-ко-многим, который будет идентифицировать вашего клиента, то есть:

user: 6
role: 4
tenant: 17

user: 6
role: 9
tenant: 18 (and so on)

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

Если вы выбрали вариант №2, ваши атрибуты [Authorize] взорвутся. Представьте себе это:

[Authorize(Roles = "TenantA:Admin", "TenantB:Admin", ...)]
public ActionResult Post(int id, SomeViewModel model) {}

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

person danludwig    schedule 21.01.2014
comment
Я использую ваш 1-й подход и добавляю еще одно свойство companyId в свой ApplicationUserRole, но я застрял в переопределении этих функций AddToRole или IsInRole. эта функция не принимает companyId. Я пытаюсь переопределить эти функции, чтобы получить имя компании на картинке. Я разместил свой вопрос [здесь] (stackoverflow.com/questions/29071800/). Если бы вы могли помочь, это было бы здорово! Спасибо - person ChengWhyNot; 16.03.2015

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

Преимущества

  1. Данные арендатора по умолчанию разделены на разные базы данных.
  2. Данные арендатора могут быть экспортированы как дамп базы данных для клиентского MI.
  3. Дизайн базы данных значительно упрощен

Недостатки

  1. Вам нужно управлять несколькими базами данных - операционная задача
  2. Вам необходимо разработать код переключения базы данных

Реализация с использованием нескольких баз данных

  1. Мы использовали базу данных конфигурации, в которой есть настройки клиента на основе кода учетной записи. Этот код учетной записи может быть получен с экрана входа в систему, или вы можете сопоставить субдомен с кодом клиента.
  2. Когда приложение запускается, вы загружаете всех клиентов в кеш (содержащий строки подключения)
  3. По каждому запросу вы должны определить клиента, а затем переключить контекст db

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

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

Что не работает

Идеальной альтернативой было бы динамически переключать ApplicationName, но хотя кажется, что это работает, ApplicationName не является потокобезопасным, поэтому это будет ненадежно:

Поскольку для всех запросов, обслуживаемых объектом HttpApplication, используется один экземпляр поставщика членства по умолчанию, вы можете иметь несколько запросов, выполняемых одновременно и пытающихся установить значение свойства ApplicationName. Свойство ApplicationName не является потокобезопасным для нескольких операций записи, а изменение значения свойства ApplicationName может привести к неожиданному поведению нескольких пользователей приложения. Мы рекомендуем вам избегать написания кода, который позволяет пользователям устанавливать свойство ApplicationName, за исключением случаев, когда это необходимо. Примером приложения, в котором может потребоваться установка свойства ApplicationName, является административное приложение, которое управляет данными членства для нескольких приложений. Такое приложение должно быть однопользовательским, а не веб-приложением.

Альтернатива: MembershipReboot

Многопользовательская среда - это сложная задача в .Net. Альтернативой встроенному членству с открытым исходным кодом является использование MembershipReboot, написанного Брок Аллен. Он имеет несколько отличных функций, включая поддержку мультитенантности прямо из коробки:

  1. управление одно- или многопользовательской учетной записью
  2. гибкий дизайн хранилища учетных записей (реляционный / SQL или объектный / NoSql), примеры с использованием EF и RavenDB
  3. идентификационные данные пользователей с учетом утверждений
  4. поддержка регистрации учетной записи, проверки электронной почты, сброса пароля и т. д.
  5. блокировка учетной записи за несколько неудачных попыток входа в систему (угадывание пароля)
  6. расширяемый шаблон для уведомлений по электронной почте
  7. настраиваемое имя пользователя, пароль и проверка адреса электронной почты
  8. система уведомлений об активности и обновлениях учетной записи (например, для аудита)
  9. связь учетной записи с внешними поставщиками удостоверений (корпоративными или социальными)
  10. поддерживает аутентификацию на основе сертификатов
  11. правильное хранение паролей (через PBKDF2)
  12. настраиваемые итерации
  13. по умолчанию - рекомендации OWASP для итераций (например, 64 КБ в 2012 году)
  14. поддержка двухфакторной аутентификации с помощью SMS-сообщений мобильного телефона или клиентских сертификатов

Наиболее распространенным вариантом использования будет интеграция этого в приложение ASP.NET или ASP.NET MVC, хотя библиотеку также можно использовать по сети в качестве службы.

Альтернатива: ServiceStack REST

Другой альтернативой, если вы создаете современные веб-приложения, активно использующие фреймворки JavaScript MVC, такие как AngularJS, EmberJS или BackboneJS, является использование сервисов ServiceStack REST. ServiceStack имеет длинный список функций аутентификации, и из моего опыта SS, я считаю, что у него очень хорошо продуманная модель API.

Аутентификация ServiceStack

person Rebecca    schedule 21.01.2014
comment
Разве это не означает, что пользователю OP, Бобу, придется создать две учетные записи пользователей, одну для компании A, а другую для компании B? - person danludwig; 21.01.2014
comment
Правильный. Но почему пользователь A из компании A будет получать доступ к информации в компании B? - person Rebecca; 21.01.2014
comment
Это похоже на требование в OP: у нас есть агентства, которые управляют несколькими арендаторами и хотят иметь возможность управлять всеми своими арендаторами с помощью одной учетной записи пользователя. Не поймите меня неправильно, я думаю, что это хорошее решение в некоторых ситуациях с несколькими арендаторами. - person danludwig; 21.01.2014
comment
У нас есть отдельная база данных для каждого арендатора с базой данных конфигурации. Данные о членстве находятся в базе данных конфигурации. - person Ted Elliott; 23.01.2014
comment
@TedElliott. Мы также используем единую базу данных конфигурации, но она просто определяет строку подключения базы данных клиента. Как вы разделяете пользователей-арендаторов, которые содержатся в config db? - person Rebecca; 23.01.2014