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

时间:2017-06-27 15:04:32

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

对于这样一般性的问题,必须有一个简单的解决方案,所以我为我的无知而道歉:

我有一个多用户网络应用程序(带有EF6的Asp.net MVC5)a.o.允许用户查看和/或修改存储在几个相关表(公司,Csearch,候选人)中的相关数据。 (有关详细信息,请参见下文)。他们不应该看到任何其他数据(例如通过篡改URL)。

我使用Asp.net Identity 2.0进行身份验证,并希望将其用于上述授权。 Userdata存储在标准的AspNetUser表中。我只为Identity和我的Business Tables使用了一个上下文。

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

我目前通过向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);

我的DataModel很简单,我使用Visual Studio 2017搭建了它 通过EF6 Code,我首先构建了一个Relational Datamodel,大致如下:

公司可以有多个搜索(一对多)。 每个搜索都可以有多个候选人(一对多)。 公司可以登录多个USERS。 用户保存在由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; }

2 个答案:

答案 0 :(得分:1)

识别出用户后,您可以确保用户只能访问自己的数据。您不能使用角色,因为这只会定义访问级别。但你可以使用索赔。

开箱即用的分离关注点。保持这种分离。您不打算直接查询Identity表。使用userManager。 永远不会使用Identity对象作为ViewModel。你可能暴露的不仅仅是你的意思。如果你保持这种分离,你会发现它实际上要容易得多。

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

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

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

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

如何添加声明

您可以在不必扩展ApplicationUser类的情况下,通过向AspNetUserClaims表添加行来向用户添加声明。类似的东西:

userManager.AddClaim(id, new Claim("UserId", UserId));

登录时,索赔将自动添加到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;
    }
}

如何阅读声明

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

var user = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = user.FindFirstValue("UserId");

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

一样

请注意,代码只是一个例子。我没有测试所有这些。它只是给你一个想法。如果有什么事情不清楚,请告诉我。

答案 1 :(得分:0)

真的非常简单。用您提供的示例公司来说明。请注意,您应该使用UserId而不是UserName,因为UserName可以更改,但UserId将始终是唯一的。)

您需要将其更改为UserId,而不是在Company表中使用UserName。然后,您将AspNetUsers表与UserId上的Company表一起加入。

例如(我更喜欢使用查询语法而不是流利的语法):

var companies = from c in db.Companies join u in db.AspNetUsers
                on c.UserId equals u.UserId
                orderby c.CompanyName
                where u.UserName = User.Identity.Name 
                select c;

如果您还需要用户名,请将其包含在您的选择

select new { Company = c, User = u.UserName };

但是,如果您希望每个公司拥有多个用户,则此模型不起作用。您需要将CompanyId添加到users表(假设用户不能成为多个公司的成员)或者如果用户可以是多个公司的成员,则创建多对多联接。

因此,不是将用户链接到公司,而是将公司链接到用户。您当前的型号仅允许每个公司一个用户。

我在这里看到的另一件事是在实体对象中使用DisplayName。这似乎表明您正在使用MVC视图中的实体,您不应该这样做。您应该创建一个单独的ViewModel。

以下是每个公司的多个用户的样子:

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

    // Link naar de Userid in Identity: AspNetUsers.Id
    // [Display(Name = "Username")] <-- Get rid of these
    // public string UserName { get; set; } <-- get rid of these
...
}

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

然后将您的查询更改为:

var companies = from c in db.Companies join u in db.AspNetUsers
            on c.CompanyId equals u.CompanyId // <-- Change to this
            orderby c.CompanyName
            where u.UserName = User.Identity.Name 
            select c;