在我认为是一项非常常见的任务的方向上,我遇到了一些绊脚石。您在哪里验证登录用户是否被允许编辑一条数据。在这种情况下,aUser
有一个 Id、EmailAddress、Name 和一个Address
对象集合。AnAddress
包含几个属性,一个是 UserId,它所属的用户的 id。应该只允许用户编辑Address
属于他们的对象。编辑Address
何时/何地检查用户是否被允许这样做。
我这周刚刚学习了 ASP.NET MVC3,并且正在将其付诸实践。我创建了一个 Web 应用程序,该应用程序使用默认的 MembershipProvider 登录用户(我将在以后将其替换为自定义的)。最终结果是登录后,User.Identity.Name
控制器中的属性返回用户的电子邮件地址。
如果用户想查看自己的详细信息,我会Addresses
调用AccountController
. 如下。
[Authorize]
public ActionResult Addresses()
{
IEnumerable<Address> addresses = _myService.GetUserAddresss(User.Identity.Name));
return View(addresses);
}
现在,如果用户想要编辑地址详细信息,他们可以调用一个操作来获取地址并显示它,并调用一个操作来保存对地址的编辑。
[Authorize]
public ActionResult Address(int id)
{
Address address= _myService.GetAddress(id));
return View(address);
}
[Authorize]
[HttpPost]
public ActionResult Address(EditAddressModel model)
{
Address address = _myService.SaveDetail(model.Address));
return View(address );
}
上述方法的缺陷在于,如果用户访问 url ../Account/Address/12
。然后他们可以查看和编辑Address
id 为 12 的(如果存在),无论他们是否创建了它。
我正在遵循 N 层方法。因此,我让控制器与服务对话,该服务与业务逻辑层对话,该服务与存储库对话,最后使用 Entity Framework 4 与数据库对话。授权检查应该在哪里进行?
我考虑了以下解决方案,但无法确定合适的方法。
理念一
在控制器类中,因为控制器有权访问 User.Identity.Name 属性。使用此属性,它可以检查从服务返回的 Address 中的 userid 是否与当前登录的用户匹配。如果没有,则显示错误页面,否则让他们正常查看/编辑。
Address
优点 - 实现简单,只需在服务返回对象后添加额外的 if 语句。控制器有权访问 User.Identity.Name。
缺点 - 数据返回给控制器,由控制器决定用户看不到它。感觉就像“只有用户可以编辑自己的地址”的业务逻辑已经渗透到控制器中。
想法 2
在业务层。控制器使用 userId 和 detailId ( _myService.GetAddress(User.Identity.Name, detailId));
) 调用服务,该服务又调用业务层。业务层有一个方法public Address GetAddress(int userId, int addressId)
当业务层被请求从数据库中获取一个Address
时,它会检查返回的地址是否属于用户并返回它。如果不是,则返回 null。因此,控制器将从服务获得空响应,并显示适当的错误消息。
优势 - 用户仅编辑其详细信息的业务逻辑位于业务层中。
缺点 - 由于业务逻辑无法访问 User.Identity.Name,因此服务和业务类中的每个方法都需要一个 userId 参数,这感觉是错误的和不必要的膨胀。
想法 3
如上,除了 Service 和 Business 层类之外,还有一个名为 UserId 的属性。这用于检查用户是否可以访问数据库资源。这可以在创建期间或调用服务之前设置。IE
[Authorize]
public ActionResult Address(int id)
{
_myService.User = User.Identity.Name;
Address address = _myService.GetAddress(id));
return View(address);
}
优势 - 用户仅编辑其详细信息的业务逻辑位于业务层中。无需将 userId 传递给每个方法调用。
缺点 - 我从未见过使用这种方法的示例。而且我觉得不对。并不是说我使用 WCF 作为服务层。但我敢肯定,如果我是的话,拥有这个额外的财产是行不通的。
想法4
访问业务层中的 User.Identity.Name,而无需将其通过服务从控制器传递到业务层。不确定是否可能。