65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 5 Identity:实现基于组的权限管理(第一部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (25投票s)

2014年2月20日

CPOL

14分钟阅读

viewsIcon

284457

为您的 ASP.NET MVC 应用程序添加基本的基于组的权限管理。适用于复杂度适中的应用程序,这些应用程序在授权权限方面需要更高的粒度,但又不足以迁移到像 Active Directory 这样重量级的解决方案。

 
 
 

leeds-castle-portcullis-500在最近的几篇文章中,我们探讨了使用和扩展 ASP.NET Identity 系统的各种方法。我们涵盖了配置数据库连接和使用 Identity 系统使用的 EF Code-First 方法的基础知识,扩展核心 IdentityUser 类以添加我们自己的自定义属性和行为(例如电子邮件地址、名字/姓氏等)。在此过程中,我们还研究了利用 ASP.NET Identity 开箱即用的基本基于角色的帐户管理

在上一篇文章中,我们弄清楚了如何扩展 IdentityRole 类,这比扩展 IdentityUser 所需的要复杂一些。

Image by Shaun Dunmall | Some Rights Reserved

在这里,我们将更进一步,在核心 ASP.NET Identity 系统提供的基本用户/角色模型之上,构建一个更高级的“权限管理”模型。

注意:如果您使用的是 2014 年 3 月 20 日发布的 Identity 2.0 RTM,则存在一些与此项目不兼容的重大更改。如果您使用的是 Visual Studio 的 MVC 项目模板中当前标准的 Identity 1.0 版本,一切都会正常。我计划在不久的将来发布一篇详细介绍如何使用 Identity 2.0 实现相同功能的文章。 

更新 2014/8/11:关于 Identity 2.0,请参阅 ASP.NET Identity 2.0:实现基于组的权限管理

在我们深入探讨之前,值得一提的是,实现一个复杂的权限管理系统并非易事。虽然我们即将研究的模型并不算太难,但在 Web 应用程序的上下文中管理大量精细的权限可能会很复杂。在将此类内容投入生产环境之前,您需要仔细思考并做好计划。

通过仔细的前期规划和精心设计的权限结构,您应该能够为您的站点在臃肿、复杂且痛苦的企业级解决方案(如 Active Directory 或 Windows 身份验证)与开箱即用的过于简单的身份管理之间找到一个折衷的方案。

显然,如果您的安全需求变得过于复杂,可能就需要考虑替代的权限管理系统了。此外,正如 Reddit 上的一位评论者所指出的,ClaimsIdentityClaimsAuthorizationManager 可能提供额外的选项。有关更多信息,请参阅 使用 .NET 4.5 的声明式安全超越用户名和角色。最后,Identity 2.0 的预览版有望提供额外的灵活性,包括两阶段身份验证。

稍后会详细介绍。首先,一些背景信息。

精细化授权权限管理 - 最低权限原则

良好的安全设计(除其他外)基于最低权限原则。也就是说,“在计算环境的特定抽象层中,每个模块(例如进程、用户或取决于主体的程序)只能访问其合法目的所需的信息和资源”

正如我们现在所知,在 ASP.NET MVC 应用程序中管理对不同功能的访问的主要方式是使用 [Authorize] 属性。我们使用 [Authorize] 装饰特定的控制器方法,并定义哪些角色可以执行该方法。例如,我们可能正在为一家公司构建一个网站。该网站可能包含许多运营或业务领域,如站点管理、人力资源、销售、订单处理等。

一个假设的 PayrollController 可能包含以下方法(除其他外)

假设的 Payroll Controller 的方法
[Authorize(Roles = "HrAdmin, CanEnterPayroll")]
[HttpPost]
public ActionResult EnterPayroll(string id)
{
    //  . . . Enter some payroll . . . 
}
  
  
[Authorize(Roles = "HrAdmin, CanEditPayroll, CanProcessPayroll")]
[HttpPost]
public ActionResult EditPayroll(string id)
{
    //  . . . Edit existing payroll entries . . . 
}
  
  
[Authorize(Roles = "HrAdmin, CanProcessPayroll")]
[HttpPost]
public ActionResult ProcessPayroll(string id)
{
    //  . . . Process payroll and cut checks . . . 
}

我们从上述信息推断,仅负责输入工资信息的基层员工无权编辑系统中已有的工作。另一方面,公司中有一些人可能需要能够编辑现有的工资信息,这可能包括特定员工部门的经理、人力资源经理本人以及负责处理工资的人员。

实际处理工资单和创建支票以进行支付的操作受到严格限制。只有人力资源经理以及“ProcessPayroll”角色的成员才能执行此操作,我们可以假设他们的人数很少。

最后,我们看到 HrAdmin 角色拥有广泛的权限,包括所有这些功能,并且可能还能够充当人力资源应用程序域内的管理员,为域内的各个用户分配这些以及其他域权限。

Identity 下应用程序授权的局限性

在当前 Identity 系统开箱即用的实现中(即使我们通过最近几篇文章进行了扩展),我们有用户和角色。作为我们安全设置的一部分,用户被分配到一个或多个角色,管理员可以添加或删除用户到各种角色。

应用程序功能对角色的访问通过 [Authorize] 属性硬编码到我们的应用程序中。因此,在生产环境中创建和修改角色几乎没有价值,除非我们实施了其他业务原因。

此外,在当前系统下,每次我们将新用户添加到系统时,都需要为用户分配特定的角色。如果我们的站点包含(例如)“管理员”、“作者”和“用户”,这并不是什么大问题。但是,对于更复杂的站点,包含多个业务领域和多个用户担任多个角色,这可能会变得痛苦。

当安全管理变得痛苦时,我们倾向于采取节省时间的行为,例如忽略最低权限原则,而是授予用户广泛的权限,以便我们不必费心(至少,如果我们没有一个勤奋的系统管理员!)。

折衷的解决方案

在本文中,我们考察了一种扩展 Identity 模型以形成折衷解决方案的方法。适用于复杂度适中的应用程序,这些应用程序在授权权限方面需要更高的粒度,但又不足以迁移到像 Active Directory 这样重量级的解决方案。

我建议在 Identity 组合中添加类似授权组的功能。组被分配各种权限组合,用户被分配到一个或多个组。

为此,我们将创建一个轻微的“幻觉”。我们将把当前我们识别为角色的东西,视为权限。然后,我们将创建这些“角色-权限”的组,并将用户分配到一个或多个组。当然,在后台,我们仍然受限于 Identity 的基本要素:用户和角色。我们还受到必须在 [Authorize] 属性中硬编码“权限”的限制。但是,我们现在可以相对精细地定义这些“角色-权限”,因为对用户分配角色权限的管理将通过分配用户到组来完成,届时该用户将承担每个特定组的所有特定权限。

基于先前工作

我以我们迄今为止构建的基础为起点,从上一篇文章中克隆了项目,在该项目中我们通过继承 IdentityRole 来扩展了我们的角色。在那篇文章中,我们没有做什么惊天动地的事情,但我们更仔细地研究了如何覆盖 ApplicationDbContextOnModelCreating() 方法,并在不损害 ASP.NET 团队创建的底层安全机制的情况下,按照我们的意愿调整 EF 和 Identity 框架。

您可以选择做同样的事情,并跟随我们一起构建,或者您可以克隆本文的最终源代码。

从 Github 获取原始源代码
获取本文的完整源代码

与之前的文章一样,我克隆了初始源代码项目后,重命名了解决方案文件、命名空间、目录和项目文件,因为在本例中,我将将其作为一个新项目推送,而不是作为对旧项目的更改。

接下来,删除现有的 Migrations 文件(但不要删除Migrations 文件夹和Configuration.cs 文件)。我们将在构建数据库之前向代码优先模型添加内容,因此我们不再需要这些文件了。

现在,我们准备开始。

添加 Group 和 ApplicationRoleGroup 模型

首先,当然,我们需要我们的 Group 类。Group 类将代表一组命名的角色,因此我们认为 Group 类拥有一系列角色。但是,由于每个 Group 可以包含零个或多个角色,并且每个角色也可以属于零个或多个组,因此这将是我们在数据库中的多对多映射。因此,我们首先需要一个中间对象 ApplicationRoleGroup,它映射多对多关系中的外键。

将以下类添加到Models 文件夹

Application Role Group 模型类
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
  
  
namespace AspNetGroupBasedPermissions.Models
{
    public class ApplicationRoleGroup
    {
        public virtual string RoleId { get; set; }
        public virtual int GroupId { get; set; }
  
        public virtual ApplicationRole Role { get; set; }
        public virtual Group Group { get; set; }
    }
}

然后将 Group 类添加为Models 中的另一个新类

Group 类
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
  
namespace AspNetGroupBasedPermissions.Models
{
    public class Group
    {
        public Group() {}
  
  
        public Group(string name) : this()
        {
            this.Roles = new List<ApplicationRoleGroup>();
            this.Name = name;
        }
  
  
        [Key]
        [Required]
        public virtual int Id { get; set; }
  
        public virtual string Name { get; set; }
        public virtual ICollection<ApplicationRoleGroup> Roles { get; set; }
    }
}

接下来,我们需要为 ApplicationUserGroup 创建类似的 Many-to-Many 映射模型。同样,每个用户可以属于零个或多个组,每个组可以包含零个或多个用户。我们已经有了 ApplicationUser 类(尽管需要对其进行一些修改),但我们需要一个 ApplicationUserGroup 类来完成映射。

添加 ApplicationUserGroup 模型

ApplicationUserGroup 类添加到 Models 文件夹

using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
  
namespace AspNetGroupBasedPermissions.Models
{
    public class ApplicationUserGroup
    {
        [Required]
        public virtual string UserId { get; set; }
        [Required]
        public virtual int GroupId { get; set; }
  
        public virtual ApplicationUser User { get; set; }
        public virtual Group Group { get; set; }
    }
}

接下来,我们需要向 ApplicationUser 添加一个 Groups 属性,以便 Entity Framework 能够理解并在使用该属性时使用它来填充组。这意味着我们需要添加一个虚拟属性,当访问该属性时,它返回一个 ICollection<ApplicationUserGroup> 的实例。

按如下方式修改现有的 ApplicationUser 类

修改后的 ApplicationUser 类
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
  
namespace AspNetGroupBasedPermissions.Models
{
    public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
            : base()
        {
            this.Groups = new HashSet<ApplicationUserGroup>();
        }
  
        [Required]
        public string FirstName { get; set; }
  
        [Required]
        public string LastName { get; set; }
  
        [Required]
        public string Email { get; set; }
  
        public virtual ICollection<ApplicationUserGroup> Groups { get; set; }
    }
}

更新 ApplicationDbContext 以反映新模型

现在我们已经对模型进行了一些扩展,我们需要更新 ApplicationDbContextOnModelCreating 方法,以便 EF 可以正确地为我们的数据库建模,并处理我们的对象。

** 整个方法会变得有些混乱和杂乱,但仔细阅读代码可以了解其大体内容。不必过于担心理解这段代码的细节——只需尝试大致了解它是如何将模型实体映射到数据库表的。 **

如果您想更好地了解 Identity(或其他尚未公开的 .NET 源代码)的内部工作原理,可以查看 Telerik 的 JustDecompile(免费)Red Gate 的 Reflector(用户免费,但现在“免费试用”)。这些优秀的逆向工程工具都能让您深入了解代码下方发生的事情。我就是这样弄清楚这部分的。

按如下方式更新 ApplicationDbContextOnModelCreating() 方法

修改后的 ApplicationDbContext 的 OnModelCreating 方法
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    if (modelBuilder == null)
    {
        throw new ArgumentNullException("modelBuilder");
    }
  
    // Keep this:
    modelBuilder.Entity<IdentityUser>().ToTable("AspNetUsers");
  
    // Change TUser to ApplicationUser everywhere else - IdentityUser 
    // and ApplicationUser essentially 'share' the AspNetUsers Table in the database:
    EntityTypeConfiguration<ApplicationUser> table = 
        modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
  
    table.Property((ApplicationUser u) => u.UserName).IsRequired();
  
    // EF won't let us swap out IdentityUserRole for ApplicationUserRole here:
    modelBuilder.Entity<ApplicationUser>()
        .HasMany<IdentityUserRole>((ApplicationUser u) => u.Roles);
    modelBuilder.Entity<IdentityUserRole>()
        .HasKey((IdentityUserRole r) => 
        new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("AspNetUserRoles");
  
    // Add the group stuff here:
    modelBuilder.Entity<ApplicationUser>()
        .HasMany<ApplicationUserGroup>((ApplicationUser u) => u.Groups);
    modelBuilder.Entity<ApplicationUserGroup>()
        .HasKey((ApplicationUserGroup r) => 
        new { UserId = r.UserId, GroupId = r.GroupId }).ToTable("ApplicationUserGroups");
  
    // And here:
    modelBuilder.Entity<Group>()
        .HasMany<ApplicationRoleGroup>((Group g) => g.Roles);
    modelBuilder.Entity<ApplicationRoleGroup>()
        .HasKey((ApplicationRoleGroup gr) => 
        new { RoleId = gr.RoleId, GroupId = gr.GroupId }).ToTable("ApplicationRoleGroups");
  
    // And Here:
    EntityTypeConfiguration<Group> groupsConfig = modelBuilder.Entity<Group>().ToTable("Groups");
    groupsConfig.Property((Group r) => r.Name).IsRequired();
  
    // Leave this alone:
    EntityTypeConfiguration<IdentityUserLogin> entityTypeConfiguration = 
        modelBuilder.Entity<IdentityUserLogin>().HasKey((IdentityUserLogin l) => 
            new { UserId = l.UserId, LoginProvider = l.LoginProvider, ProviderKey = 
                l.ProviderKey }).ToTable("AspNetUserLogins");
  
    entityTypeConfiguration.HasRequired<IdentityUser>((IdentityUserLogin u) => u.User);
    EntityTypeConfiguration<IdentityUserClaim> table1 = 
        modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
    table1.HasRequired<IdentityUser>((IdentityUserClaim u) => u.User);
  
    // Add this, so that IdentityRole can share a table with ApplicationRole:
    modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
  
    // Change these from IdentityRole to ApplicationRole:
    EntityTypeConfiguration<ApplicationRole> entityTypeConfiguration1 = 
        modelBuilder.Entity<ApplicationRole>().ToTable("AspNetRoles");
    entityTypeConfiguration1.Property((ApplicationRole r) => r.Name).IsRequired();
}

接下来,我们需要在 ApplicationDbContext 上显式添加一个 Groups 属性。同样,这需要是一个虚拟属性,但在这种情况下,返回类型是 ICollection<Group>

将 Groups 属性添加到 ApplicationDbcontext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    // Add an instance IDbSet using the 'new' keyword:
    new public virtual IDbSet<ApplicationRole> Roles { get; set; }
  
    // ADD THIS:
    public virtual IDbSet<Group> Groups { get; set; }
  
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
  
    }
  
  
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Code we just added above is here . .  .
    }
  
    // Etc . . .
}

将组管理项添加到 Identity Manager

现在,让我们添加一些代码来帮助我们管理与组相关的各种功能,以及与组相关的用户和角色的管理(“权限”)。仔细查看下面的方法,了解大多数情况下发生了什么,因为对组执行的某些操作可能会对安全范围产生潜在影响。

例如,当我们删除一个组时,我们还需要

  • 将所有用户从组中删除。请记住,这里有一个中间表或“关系表”的外键关系——需要先删除相关记录,否则我们通常会收到键约束错误。
  • 将该组中的所有角色删除。请记住,这里有一个中间表或“关系表”的外键关系——需要先删除相关记录,否则我们通常会收到键约束错误。
  • 从每个用户中删除角色,除非该用户由于属于另一个组而具有相同的角色(这很难思考!)。

同样,当我们向组添加一个角色(“权限”)时,我们需要更新该组中的所有用户以反映添加的权限。

将以下方法添加到现有的 IdentityManager 类的底部

将 Group 方法添加到 Identity Manager 类
public void CreateGroup(string groupName)
{
    if (this.GroupNameExists(groupName))
    {
        throw new System.Exception("A group by that name already exists in the database. Please choose another name.");
    }
  
    var newGroup = new Group(groupName);
    _db.Groups.Add(newGroup);
    _db.SaveChanges();
}
  
  
public bool GroupNameExists(string groupName)
{
    var g = _db.Groups.Where(gr => gr.Name == groupName);
    if (g.Count() > 0)
    {
        return true;
    }
    return false;
}
  
  
public void ClearUserGroups(string userId)
{
    this.ClearUserRoles(userId);
    var user = _db.Users.Find(userId);
    user.Groups.Clear();
    _db.SaveChanges();
}
  
  
public void AddUserToGroup(string userId, int GroupId)
{
    var group = _db.Groups.Find(GroupId);
    var user = _db.Users.Find(userId);
  
    var userGroup = new ApplicationUserGroup()
    {
        Group = group,
        GroupId = group.Id,
        User = user,
        UserId = user.Id
    };
  
    foreach (var role in group.Roles)
    {
        _userManager.AddToRole(userId, role.Role.Name);
    }
    user.Groups.Add(userGroup);
    _db.SaveChanges();
}
  
  
public void ClearGroupRoles(int groupId)
{
    var group = _db.Groups.Find(groupId);
    var groupUsers = _db.Users.Where(u => u.Groups.Any(g => g.GroupId == group.Id));
  
    foreach (var role in group.Roles)
    {
        var currentRoleId = role.RoleId;
        foreach (var user in groupUsers)
        {
            // Is the user a member of any other groups with this role?
            var groupsWithRole = user.Groups
                .Where(g => g.Group.Roles
                    .Any(r => r.RoleId == currentRoleId)).Count();
  
            // This will be 1 if the current group is the only one:
            if (groupsWithRole == 1)
            {
                this.RemoveFromRole(user.Id, role.Role.Name);
            }
        }
    }
    group.Roles.Clear();
    _db.SaveChanges();
}
  
  
public void AddRoleToGroup(int groupId, string roleName)
{
    var group = _db.Groups.Find(groupId);
    var role = _db.Roles.First(r => r.Name == roleName);
    var newgroupRole = new ApplicationRoleGroup()
    {
        GroupId = group.Id,
        Group = group,
        RoleId = role.Id,
        Role = (ApplicationRole)role
    };
  
    group.Roles.Add(newgroupRole);
    _db.SaveChanges();
  
    // Add all of the users in this group to the new role:
    var groupUsers = _db.Users.Where(u => u.Groups.Any(g => g.GroupId == group.Id));
    foreach (var user in groupUsers)
    {
        if(!(_userManager.IsInRole(user.Id, roleName)))
        {
            this.AddUserToRole(user.Id, role.Name);
        }
    }
}
  
  
public void DeleteGroup(int groupId)
{
    var group = _db.Groups.Find(groupId);
  
    // Clear the roles from the group:
    this.ClearGroupRoles(groupId);
    _db.Groups.Remove(group);
    _db.SaveChanges();
}

现在我们已经拥有了在后端管理用户、组和角色(“权限”)之间关系所需的核心代码。现在我们需要设置我们的 Migrations 配置​​文件,以便在运行 EF Migrations 时正确地填充数据库。

更新 Migrations 配置​​文件以填充数据库

大部分基本模型现在已到位,我们可以运行 EF Migrations 并构建我们的修改后的数据库。但在那之前,我们要更新我们的 Migrations Configuration 类,以便我们在运行 EF Migrations 时用最少量的必需数据来填充数据库。请记住,我们的网站不允许“公共”注册。因此,至少我们需要像以前一样,用一个初始管理员级别的用户来填充它。

与以前不同的是,我们改变了分配和管理角色的方式。今后,我们需要用一个或多个初始组来填充我们的初始用户,并用至少一个具有足够管理员权限的组来填充,这样我们的初始用户就可以在此基础上进行操作。

此代码的编写方式有很多种。此外,根据您的应用程序要求,数据库的填充方式可能会成为一项广泛的计划工作(还记得那个关于更复杂的授权模型需要更多前期规划的说法吗?)。

在这里,我们将更新我们的 Configuration 类,并添加一些新方法。我们将添加一个初始用户、一些可能使用的组,以及一些与管理安全和授权相关的角色。

更新的 Migrations 配置​​文件
internal sealed class Configuration 
    : DbMigrationsConfiguration<ApplicationDbContext>
{
    IdentityManager _idManager = new IdentityManager();
    ApplicationDbContext _db = new ApplicationDbContext();
  
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }
  
  
    protected override void Seed(ApplicationDbContext context)
    {
        this.AddGroups();
        this.AddRoles();
        this.AddUsers();
        this.AddRolesToGroups();
        this.AddUsersToGroups();
    }
  
     
    string[] _initialGroupNames = 
        new string[] { "SuperAdmins", "GroupAdmins", "UserAdmins", "Users" };
  
    public void AddGroups()
    {
        foreach (var groupName in _initialGroupNames)
        {
            _idManager.CreateGroup(groupName);
        }
    }
  
  
    void AddRoles()
    {
        // Some example initial roles. These COULD BE much more granular:
        _idManager.CreateRole("Admin", "Global Access");
        _idManager.CreateRole("CanEditUser", "Add, modify, and delete Users");
        _idManager.CreateRole("CanEditGroup", "Add, modify, and delete Groups");
        _idManager.CreateRole("CanEditRole", "Add, modify, and delete roles");
        _idManager.CreateRole("User", "Restricted to business domain activity");
    }
  
  
    string[] _superAdminRoleNames = 
        new string[] { "Admin", "CanEditUser", "CanEditGroup", "CanEditRole", "User" };
    string[] _groupAdminRoleNames =
        new string[] { "CanEditUser", "CanEditGroup", "User" };
    string[] _userAdminRoleNames =
        new string[] { "CanEditUser", "User" };
    string[] _userRoleNames =
        new string[] { "User" };
    void AddRolesToGroups()
    {
        // Add the Super-Admin Roles to the Super-Admin Group:
        var allGroups = _db.Groups;
        var superAdmins = allGroups.First(g => g.Name == "SuperAdmins");
        foreach (string name in _superAdminRoleNames)
        {
            _idManager.AddRoleToGroup(superAdmins.Id, name);
        }
  
        // Add the Group-Admin Roles to the Group-Admin Group:
        var groupAdmins = _db.Groups.First(g => g.Name == "GroupAdmins");
        foreach (string name in _groupAdminRoleNames)
        {
            _idManager.AddRoleToGroup(groupAdmins.Id, name);
        }
  
        // Add the User-Admin Roles to the User-Admin Group:
        var userAdmins = _db.Groups.First(g => g.Name == "UserAdmins");
        foreach (string name in _userAdminRoleNames)
        {
            _idManager.AddRoleToGroup(userAdmins.Id, name);
        }
  
        // Add the User Roles to the Users Group:
        var users = _db.Groups.First(g => g.Name == "Users");
        foreach (string name in _userRoleNames)
        {
            _idManager.AddRoleToGroup(users.Id, name);
        }
    }
  
  
    // Change these to your own:
    string _initialUserName = "jatten";
    string _InitialUserFirstName = "John";
    string _initialUserLastName = "Atten";
    string _initialUserEmail = "jatten@typecastexception.com";
    void AddUsers()
    {
        var newUser = new ApplicationUser()
        {
            UserName = _initialUserName,
            FirstName = _InitialUserFirstName,
            LastName = _initialUserLastName,
            Email = _initialUserEmail
        };
  
        // Be careful here - you  will need to use a password which will 
        // be valid under the password rules for the application, 
        // or the process will abort:
        _idManager.CreateUser(newUser, "Password1");
    }
  
  
    // Configure the initial Super-Admin user:
    void AddUsersToGroups()
    {
        var user = _db.Users.First(u => u.UserName == _initialUserName);
        var allGroups = _db.Groups;
        foreach (var group in allGroups)
        {
            _idManager.AddUserToGroup(user.Id, group.Id);
        }
    }
}

如上所示,我(相当随意地)决定设置一些与用户/组/角色域相关的初始组和角色。如果我们已经知道应用程序其余部分的域结构,我们可能希望将其他角色(“权限”)作为我们配置的一部分,因为角色需要使用 [Authorize] 属性硬编码到我们的控制器中。我们越早确定我们应用程序安全模型的角色结构,就越好。然而,您需要在此处仔细权衡粒度和可管理性。

目前,我们已经有了足够好的起点,可以运行 EF Migrations 并查看我们的数据库是否已成功构建。

运行 Migrations 并构建数据库

如前所述,在执行此操作时,我删除了之前的 Migration 文件,但保留了 Migrations 文件夹(以及修改后的 Configuration.cs 文件)。因此,为了执行迁移,我只需在 Package Manager Console 中输入以下内容:

添加新迁移
PM> Add-Migration init

这会生成一个新的迁移。接下来

构建数据库
PM> Update-Database

如果一切顺利,我们应该能够打开 Visual Studio Server Explorer 中的数据库,看看我们做得怎么样。您应该会看到类似以下的内容

VS Server Explorer 中的数据库

vs-server-explorer-database-view

看起来一切顺利!

下一步:控制器、视图模型和视图

本文篇幅过长,所以我决定将其分成两部分。在本文中,我们弄清楚了如何在我们的应用程序中建模用户、组和角色(“权限”),并通过 EF Code-First 和 Migrations 在数据库中进行扩展。

接下来,我们将开始将所有这些内容整合到我们应用程序的业务部分。

下一步:第二部分 - 控制器、视图模型和视图 --->

其他资源和感兴趣的项目

 
 
 

© . All rights reserved.