如何使用asp.net Identity 2.0授权用户只能查看自己的记录

Cha*_* M. 3 c# linq asp.net-mvc asp.net-identity

对于这样一个普遍的问题一定有一个简单的解决方案,所以我为我的无知提前道歉:

我有一个多用户 Web 应用程序(带有 EF6 的 Asp.net MVC5),允许用户查看和/或修改存储在多个相关表(Company、Csearch、Candidate)中的相关数据。(更多详情请参见下文)。他们不应该看到任何其他数据(例如通过篡改 URL)。

我使用 Asp.net Identity 2.0 进行身份验证,并且也想将其用于上述授权。用户数据存储在标准的 AspNetUser 表中。我仅对身份表和业务表使用一种上下文。

我想我必须使用角色或声明来解决这个问题,但我找不到任何关于如何做到这一点的指导。有人能指出我正确的方向吗?

我目前已经通过向 CompanyController 添加 LINQ 条件来解决它(对于公司模型),但这似乎不是解决问题的非常安全和正确的方法。

public ActionResult Index(int? id, int? csearchid)
        {
            var companies = db.Companies
              .OrderBy(i => i.CompanyName)
              .Where(t => t.UserName == User.Identity.Name);
        return View(companies);
Run Code Online (Sandbox Code Playgroud)

我的数据模型很简单,我使用 Visual Studio 2017 搭建了它的脚手架,首先通过 EF6 代码,我构建了一个关系数据模型,大致如下:

一个公司可以有多个搜索(一对多)。每个搜索可以有多个候选者(一对多)。一个公司可以有多个登录用户。用户保存在 ASP.Net Identity 生成的 AspNetUsers 表中。

我的公司模型如下:

public class Company
{
    public int CompanyID { get; set; }

    // Link naar de Userid in Identity: AspNetUsers.Id
    [Display(Name = "Username")]
    public string UserName { get; set; }        
    public string CompanyName { get; set;}
    public string CompanyContactName { get; set; }
    [DataType(DataType.EmailAddress)]        
    public string CompanyEmail { get; set; }
    public string CompanyPhone { get; set; }        

    [Timestamp]
    public byte[] RowVersion { get; set; }

    //One to Many Navigatie links
    public virtual ICollection<Csearch> Csearches { get; set; }
Run Code Online (Sandbox Code Playgroud)

小智 5

一旦识别出用户,您就可以确保该用户只能访问自己的数据。您不能为此使用角色,因为这只会定义访问级别。但您可以使用声明。

开箱即用的存在关注点分离。保持这种分离。您不应该直接查询 Identity 表。为此使用 userManager。也不要使用 Identity 对象作为 ViewModel。你可能会暴露比你想要暴露的更多的东西。如果你保持这种分离,你会发现它实际上要容易得多。

身份上下文包含识别用户的所有数据,业务上下文包含所有业务信息,包括用户信息。您可能认为这是多余的,但登录用户与业务用户确实没有任何共同之处。登录电子邮件地址可能与business.user.emailaddress不同(这两种情况下电子邮件地址的含义是什么?)。还要考虑用户无法(不再)登录的可能性。

根据经验,始终考虑信息是身份的一部分还是业务的一部分。

什么时候需要 ApplicationUser?仅适用于当前用户或管理用户时。查询用户时,请始终使用business.user。因为您需要的所有信息都应该在那里可用。

对于当前用户,添加包含您需要的信息的声明。声明的优点是您不必在每次调用时查询数据库来检索此信息,例如相应的 UserId 和(显示)UserName。

如何添加声明

您可以通过向 AspNetUserClaims 表添加一行来向用户添加声明,而无需扩展 ApplicationUser 类。就像是:

userManager.AddClaim(id, new Claim("UserId", UserId));
Run Code Online (Sandbox Code Playgroud)

登录后,声明将自动添加到 ClaimsIdentity 中。

您还可以为扩展 ApplicationUser 的属性添加声明:

public class ApplicationUser : IdentityUser
{
    public int UserId { get; set; }

    public string DisplayUserName { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

        // Add custom user claims here
        userIdentity.AddClaim(new Claim("UserId", UserId));
        userIdentity.AddClaim(new Claim("DisplayUserName", DisplayUserName));

        return userIdentity;
    }
}
Run Code Online (Sandbox Code Playgroud)

如何阅读权利要求

在控制器中,您可以使用如下代码读取声明:

var user = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = user.FindFirstValue("UserId");
Run Code Online (Sandbox Code Playgroud)

您可以在查询中使用 userId 来过滤当前用户的数据,甚至可以使用business.users 作为检索数据的唯一条目。喜欢db.Users(u => u.Id == userId).Companies.ToList();

请注意,该代码只是一个示例。我没有测试全部。这只是为了给你一个想法。如果有不清楚的地方,请告诉我。