Идентичность .NET Core 2.1: создание таблицы для каждой таблицы ролей + мостов M: M - PullRequest
0 голосов
/ 29 сентября 2018

У меня проблемы с определением наилучшего дизайна, который соответствует моим потребностям в отношении авторизации на основе ролей с использованием Identity в проекте .NET Core 2.1.

Я уже расширил класс User с Identity с помощью ApplicationUserучебный класс.Мне нужно 5 разных ролей для управления доступом к различным функциям приложения:

Администратор, Учитель, Студент, Родитель и Супервизор

Все общие атрибуты хранятся в User и ApplicationUser, но япо-прежнему требуются разные отношения с другими таблицами в зависимости от роли пользователя.

  • Пользователь в роли Учитель связан с 1-N School
  • Пользователь в роли Ученик связан с 1-N GroupOfStudents(но не непосредственно в школу)
  • Пользователь с ролью Родитель связан с 1-N учеником (но не со школой)
  • ...

Другое требование заключается в том, что пользователь должен быть в состоянии роли 1-N.

Что было бы лучшим в моем случае?

Есть ли что-то, чего мне не хватает в функцияхof Identity?

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

Я думал об использовании "бридтаблица ge ", чтобы связать пользователя с другими таблицами для каждой роли.Имейте отношение многие-ко-многим между ApplicationUser и таблицей мостов и отношения 0-1 между таблицей мостов и отдельными таблицами для каждой роли.Но это тоже не очень помогает, поскольку каждая запись будет содержать одинаковое количество пустых полей.

Я довольно новичок в .NET Core и особенно в Identity, возможно, мне не хватает некоторых ключевых слов, чтобы провести эффективное исследование, потому чтомне кажется, что это действительно базовая система (ничего особенного в требованиях).

Спасибо за чтение!

РЕДАКТИРОВАТЬ: У меня действительно нетошибка сейчас, так как я пытаюсь выяснить лучшие практики, прежде чем углубляться в проект.Поскольку я впервые сталкиваюсь с подобными требованиями, я пытаюсь найти документацию о преимуществах и недостатках.

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

public class ApplicationUser : IdentityUser
{
    public string CustomTag { get; set; }
    public string CustomTagBis { get; set; }
}
    public class Teacher : ApplicationUser
{
    public string TeacherIdentificationNumber { get; set; }
    public ICollection<Course> Courses { get; set; }
}
public class Student : ApplicationUser
{
    public ICollection<StudentGroup> Groups { get; set; }
}
public class Parent : ApplicationUser
{
    public ICollection<Student> Children { get; set; }
}
public class Course
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Category { get; set; }
}
public class StudentGroup
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Это создаст базу данных, имеющую одну большую таблицу для Пользователя, содержащую все атрибуты:

Сгенерированная таблица пользователей image

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

Меня беспокоит то, что для каждой записи у меня будет огромное количество «неподходящих полей», которые останутся пустыми.Допустим, что на 1000 пользователей 80% пользователей являются студентами.Каковы последствия наличия 800 строк, содержащих: - пустой ParentId FK - пустой номер TeacherIdentificationNumber

И это лишь небольшая часть содержимого моделей.Он не «чувствует» себя правильно, я не прав?

Нет ли лучшего способа спроектировать сущности так, чтобы таблица User содержала только общие атрибуты для всех пользователей (как и должно быть?) и все еще сможет связать каждого пользователя с другой таблицей, которая свяжет пользователя с 1-N таблицами Teacher / Student / Parent / ... table?

Схема подхода Table-Per-Hierarchy image

РЕДАКТИРОВАТЬ 2: Используя ответ Марко, я попытался использовать подход Table-Per-Type.При изменении моего контекста для реализации подхода Table-Per-Type я столкнулся с этой ошибкой, когда хотел добавить миграцию:

«Тип сущности IdentityUserLogin требует определения первичного ключа.»

Я полагаю, что это происходит, потому что я удалил:

base.OnModelCreating(builder);

В результате получился следующий код:

protected override void OnModelCreating(ModelBuilder builder)
{
        //base.OnModelCreating(builder);
        builder.Entity<Student>().ToTable("Student");
        builder.Entity<Parent>().ToTable("Parent");
        builder.Entity<Teacher>().ToTable("Teacher");
}

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

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

Используя этот подход, у меня естьСхема, которая выглядит следующим образом: Подход таблицы к типу image

Исправьте меня, если я ошибаюсь, но обе методики соответствуют моим требованиям, и это больше о предпочтениях дизайна?Это не имеет большого значения ни в архитектуре, ни в особенностях идентификации?


Что касается третьего варианта, я подумывал использовать другой подход, но я не слишком уверен в этом.

Может ли подобный дизайн соответствовать моим требованиям и действителен ли он? Под действительным, я имею в виду, кажется странным связывать сущность учителя с ролью, а не с пользователем.Но в некотором смысле сущность учителя представляет функции, которые потребуются пользователю в роли учителя.

Роль в сущностях image

Я не являюсьно слишком уверен в том, как реализовать это с ядром EF и как переопределение класса IdentityRole повлияет на функции Identity.Я нахожусь на этом, но еще не понял это.

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Я предлагаю вам воспользоваться новыми возможностями ядра asp.net и новой платформой Identity.Существует много документации о безопасности .

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

Наилучший подход состоит в том, чтобы не смешивать контексты. Сохранять разделение интересов: контекст идентификации (с помощью UserManager) и бизнес-контекст (школа, ваш DbContext).

Поскольку помещение таблицы ApplicationUser в ваш «бизнес-контекст» означает, что вы напрямую обращаетесь к контексту Identity.Это не тот способ, которым вы должны использовать идентичность.Используйте UserManager для запросов, связанных с IdentityUser.

Чтобы заставить его работать, вместо того, чтобы наследовать таблицу ApplicationUser, создайте пользовательскую таблицу в школьном контексте.Это не копия, а новая таблица.На самом деле единственное, что общего - это поле UserId.

Проверьте мой ответ здесь , чтобы узнать о более подробном дизайне.

Переместите такие поля, как TeacherIdentificationNumber, из ApplicationUser,Вы можете добавить это как утверждение к пользователю (таблица AspNetUserClaims):

new Claim("http://school1.myapp.com/TeacherIdentificationNumber", 123);

или сохранить его в школьном контексте.

Также вместо ролей рассмотрите возможность использования утверждений, где вы можетеразличайте утверждения по имени типа (например, http://school1.myapp.com/role):

new Claim("http://school1.myapp.com/role", "Teacher");
new Claim("http://school2.myapp.com/role", "Student");

. Хотя я думаю, что в вашем случае лучше хранить информацию в школьном контексте.

Суть в том, чтобы сохранить контекст Identityкак есть и вместо этого добавьте таблицы в школьный контекст. Вам не нужно создавать две базы данных, просто не добавляйте связи между контекстами. Единственное, что связывает их, - это UserId. Но вам не нужен фактическийсвязь с базой данных для этого.

Используйте UserManager и т. д. для запросов идентификации и школьный контекст для вашего приложения. Если не для аутентификации, вы не должны использовать контекст идентификации.


Теперь длядизайн, создайте одну пользовательскую таблицу, которая имеет соответствующее поле UserId, чтобы связать текущего пользователя. Добавляйте поля, такие как имя и т. д., только когда вы хотите показать это (в отчете).

Добавьте таблицу для ученика, учителя и т. Д., Где вы используете составной ключ: School.Id, User.Id.Или добавьте общий идентификатор и используйте уникальное ограничение для комбинации School.Id, User.Id.

Когда пользователь присутствует в таблице, это означает, что пользователь является учеником в школе x или учителем вшкола г.Нет необходимости в ролях в контексте Identity.

С помощью свойств навигации вы можете легко определить «роль» и получить доступ к полям этой «роли».

0 голосов
/ 30 сентября 2018

То, что вы делаете, полностью соответствует вашим требованиям.То, что вы сейчас внедрили, называется Table-Per-Hierarchy.Это подход по умолчанию, который применяется Entity Framework при обнаружении своей модели.

Альтернативный подход будет Table-Per-Type.В этом случае Entity Framework создаст 4 таблицы.

  1. Таблица пользователей
  2. Таблица учеников
  3. Таблица учителей
  4. Таблица родителей

Поскольку всеэти объекты наследуются от ApplicationUser, база данных будет генерировать отношения FK между ними и их родительским классом.

Чтобы реализовать это, вам нужно изменить свой DbContext:

public class FooContext : DbContext
{
    public DbSet<ApplicationUser> Users { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>().ToTable("Students");
        modelBuilder.Entity<Parent>().ToTable("Parents");
        modelBuilder.Entity<Teacher>().ToTable("Teachers");
    }
}

Это должно бытьсамый нормализованный подход.Тем не менее, существует третий подход, где вы в конечном итоге получите 3 таблицы, и родительский класс ApplicationUser будет отображен в его конкретные реализации.Тем не менее, я никогда не реализовывал это с Asp.Net Identity, поэтому я не знаю, будет ли это работать или будет работать, и если вы столкнетесь с некоторыми ключевыми конфликтами.

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