【问题标题】:Correct use of Microsoft.AspNet.Identity 2.0正确使用 Microsoft.AspNet.Identity 2.0
【发布时间】:2015-12-04 23:58:37
【问题描述】:

我迷失了使用 MVC 5 模板附带的身份验证方法。

我需要将 CreateBy 用户包含在一个名为 client 的实体中,所以经过一些研究后我得出了这个结论:

型号:

[Table("Clients")]
public partial class Client
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual int UserCreated_Id { get; set; }

    [ForeignKey("UserCreated_Id")]
    public virtual ApplicationUser UserCreated { get; set; }
}

控制器方法:

client.UserCreated_Id = User.Identity.GetUserId<int>();

但我必须更改身份模型中的几乎所有内容:

来自

public class ApplicationUser : IdentityUser

public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>

因此有近 30 次更改。

但现在我有 2 个 DbContext:

身份上下文:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext() : base("IPDB") {}

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

我的应用程序上下文:

public class MyDbContext : DbContext
{
    public MyDbContext() : base("IPDB")
    {

        // Tells Entity Framework that we will handle the creation of the database manually for all the projects in the solution
        Database.SetInitializer<MyDbContext>(null);
    }

    public DbSet<Client> Clients { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // ANOTHER CHANGE I HAD TO MADE TO BE ABLE TO SCAFFOLDING
        modelBuilder.Entity<ApplicationUserLogin>().HasKey<int>(l => l.UserId);
        modelBuilder.Entity<ApplicationRole>().HasKey<int>(r => r.Id);
        modelBuilder.Entity<ApplicationUserRole>().HasKey(r => new { r.RoleId, r.UserId });
    }
}

我现在的问题是:

  • 我需要 2 个 DbContext 吗?
  • 我是否正确地将用户关联到客户端实体?
  • 我需要创建一个所有用户的列表,以及其他信息,我会从 2 DbContext 中读取信息吗?

拜托,我需要一些明确的指导,因为我现在很困惑,我真的很喜欢编写出色的代码,但我认为事实并非如此。

【问题讨论】:

    标签: c# asp.net-mvc asp.net-identity dbcontext


    【解决方案1】:

    ApplicationUser 上的 Id 类型

    ASP.NET Identity 2.0 非常灵活,同时还提供了一些在大多数情况下都可以使用的默认实现。 MVC 5 模板在大多数地方使用默认实现,但在某些情况下添加了一些额外的东西以使定制更容易。

    在内部 ASP.NET 标识始终使用 IdentityUser&lt;...&gt;。所有这些模板参数都提供了一种选择您自己的密钥类型的方法,这意味着您还必须选择自己的 IdentityUserRole 类型(因为它包含密钥)。

    还有使用字符串作为键的便利默认实现。字符串的值是 GUID 的字符串表示形式。

    public class IdentityUser : 
      IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
    {
      /// <summary>
      /// Constructor which creates a new Guid for the Id
      /// </summary>
      public IdentityUser()
      {
        this.Id = Guid.NewGuid().ToString();
      }
    

    在数据库中将字符串作为主键并不常见,但对于安全敏感的东西来说,它是有意义的。不存在在数据库之间复制的数据存在 ID 冲突的风险,从而为用户提供错误的权限。

    在您的情况下,您需要选择:使用带有字符串 PK 的默认实现或将 id 更改为 int。在后一种情况下,您必须更改身份模型中的“几乎所有内容”,正如您所发现的那样。尽管我认为您可以不实现自己的ApplicationRole。应该可以将ApplicationUser 声明为

    public class ApplicationUser: IdentityUser<int, IdentityUserLogin<int>, IdentityUserRole<int>....>
    

    数据库上下文

    在大多数情况下,单个DbContext 对应用程序来说是可以的。如果使用多个 DbContext,它们应该用于彼此不相关的不同数据集。当您将Client 类设置为与ApplicationUser 相关时,它们应该都在同一个DbContext 中。该模板试图传达这样一种思想,即应用程序上下文的主要DbContext 应该通过将其命名为ApplicationDbContext 来继承IdentityDbContext&lt;&gt;。使用那个,并用你自己的东西扩展它。我通常将其重命名并将其移至其他位置,但我仍然只有一个 DbContext 并让它继承 IdentityDbContext&lt;&gt;

    推出您自己的身份解决方案

    除非你真的、真的知道你在做什么 - 不要推出你自己的身份解决方案。使用已通过外部审查加强安全性的现有产品。如果您对 ASP.NET Identity 不满意,可以查看Brock Allen's Identity reboot

    为了可信度(是的,我今天是赏金猎人):The official ASP.NET documentation 链接到我的blog

    【讨论】:

    • 您好,谢谢,选择遵循哪种方式真的不容易,我仍然不清楚在我的项目中使用一个简单的 Microsoft Identity 实现,因此我可以使用登录的用户与我的其他用户关联实体...
    • 您还有什么具体问题可以补充到答案中吗?
    • 最简单的方法:将Client.UserCreated_Id改为字符串。这将允许您使用默认的 ASP.NET 标识实现。使用一个 DbContext。
    • 如果您使用的是 EF:ApplicationContext.Clients.Select(c =&gt; new {c.Id, c.UserCreated.UserName })。如果您没有在应用程序的其余部分使用 EF,那么正确的方法是在您拥有的任何数据库访问权限之上自己实现 IUserStore&lt;&gt;
    • 是的,如果您使用 EF 路径,您应该将所有实体添加到 ApplicationContext。
    【解决方案2】:

    我需要 2 个 DbContext 吗?

    对于某些人来说,为 Identity 和其他应用程序的其余部分拥有一个 DbContext 被认为是一种很好的做法。但是这不是强制性的,我认为没有必要。

    您不应该有大的 dbcontext,较小的上下文更快。这意味着有时您必须创建多个 dbcontext。我相信每个上下文 50 个实体就足够了。

    我是否正确地将用户关联到客户端实体?

    你不是(在我看来)。 ApplicationUser 使用Guid(默认)来表示其主键值,而不是Int32。所以,而不是这个:

    public virtual int UserCreated_Id { get; set; }
    
    [ForeignKey("UserCreated_Id")]
    public virtual ApplicationUser UserCreated { get; set; } 
    

    你应该使用这个(记得删除UserCreated_Id中的虚拟):

    public Guid UserCreated_Id { get; set; }
    
    [ForeignKey("UserCreated_Id")]
    public virtual ApplicationUser UserCreated { get; set; } 
    

    在您的上下文中,这应该足够了:

    public class ApplicationUser : IdentityUser
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    

    我需要创建一个所有用户的列表,以及其他信息,将 我从 2 DbContext 中读取信息?

    是的,您可能会,但我认为这对性能影响不大,因为考虑到您当时只使用 1 个 dbcontext,它不会导致任何额外的数据库查询。

    拜托,我需要一些明确的指导,因为我现在很困惑 而且我真的很喜欢编写出色的代码,但我认为事实并非如此。

    你喜欢干净的代码吗?所以,这是我的提示(基于我的意见):忘记 ASP.NET 身份!身份是一个糟糕的解决方案,它不是解耦、混乱和不必要的。

    您可以使用自定义用户实体创建自己的登录系统。使用此https://crackstation.net/hashing-security.htm#aspsourcecode 对密码进行哈希处理,并使用此MVC Authentication - Easiest Way 创建 OWIN 身份验证

    【讨论】:

    • Muito Obrigado Fabio :) 如何操作记录的用户信息,以便将其与客户端关联或提取信息?如果我不从一个干净简单的示例开始,我会寻找一个我不知道可能很复杂的新登录系统。
    • 完全不同意向显然没有准备好这样做的人推荐的“创建自己的登录系统”。此外,我以前从未听说过“Microsoft 建议每个上下文最多 50 个实体”。你能提供一个参考吗?
    • 另一个不正确的陈述“应用程序用户使用 Guid”。它可以使用Guid。在提供的代码中,您可以看到ApplicationUserint 作为TKey 通用参数。
    • 从 EF 中的 2 个不同 DbContext 读取数据将创建 2 个对 DB 的单独请求。所以说“它不会引起任何额外的数据库查询”是不正确的。
    • @trailmax 你读过这个问题了吗? ApplicationUser 默认使用 Guid。 OP不想做很多改变。在他的示例中,他使用Int32,这就是为什么他必须进行很多更改...此外,我可以有多个 dbcontext,考虑到我当时只使用 1 个 dbcontext,它不会导致任何性能问题.