对于这样一般性的问题,必须有一个简单的解决方案,所以我为我的无知而道歉:
我有一个多用户网络应用程序(带有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; }
答案 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;