前言:这是一个有些哲学意味的问题。我更关心的是“正确”的方法,而不是“一种”方法。
假设我有一些产品,并且有一个ASP.NET MVC应用程序来执行对这些产品的CRUD操作:
mysite.example/products/1
mysite.example/products/1/edit
我正在使用仓储模式,因此这些产品来自哪里并不重要:
public interface IProductRepository
{
IEnumberable<Product> GetProducts();
....
}
此仓库描述了一个用户列表,以及他们管理的产品(用户和产品之间存在多对多的关系)。在应用程序的其他地方,超级管理员对用户执行CRUD操作,并管理他们被允许管理的产品之间的关系。任何人都可以查看任何产品,但只有被指定为特定产品“管理员”的用户才可以调用例如编辑操作。
我应该如何在ASP.NET MVC中实现这一点?除非我错过了什么,否则我不能使用内置的ASP.NET Authorize属性,因为首先我需要为每个产品设置不同的角色,其次我不知道要检查哪个角色,直到从存储库中检索到我的产品。
显然,您可以从此场景推广到大多数内容管理场景-例如,只允许用户编辑自己的论坛帖子。 StackOverflow用户只能编辑自己的问题-除非他们拥有2000或更多的声望...
例如,最简单的解决方案是:
public class ProductsController
{
public ActionResult Edit(int id)
{
Product p = ProductRepository.GetProductById(id);
User u = UserService.GetUser(); // Gets the currently logged in user
if (ProductAdminService.UserIsAdminForProduct(u, p))
{
return View(p);
}
else
{
return RedirectToAction("AccessDenied");
}
}
}
我的问题:
- 部分代码需要重复使用 - 想象一下,有几个操作(Update、Delete、SetStock、Order、CreateOffer)取决于用户和产品的关系。你需要多次复制粘贴。
- 不太容易测试 - 你必须模拟每个测试中的四个对象。
- 控制器似乎并不需要检查用户是否允许执行操作。我更喜欢一个更加可插拔(例如通过属性的AOP)的解决方案。然而,这是否意味着您必须两次SELECT产品(在AuthorizationFilter和Controller中各一次)?
- 如果用户不被允许进行此请求,是否最好返回403?如果是这样,我该如何做呢?
我可能会随着自己的想法而更新,但我非常渴望听到你的想法!
提前致谢!
编辑
只是在这里添加一些细节。我遇到的问题是,我希望业务规则“只有具有权限的用户才能编辑产品”仅包含在一个地方。我觉得决定用户是否可以获取或POST到Edit操作的相同代码也应该负责确定是否在Index或Details视图上呈现“Edit”链接。也许这是不可能的/不可行的,但我觉得应该这样...
第二次编辑
在这个问题上开始了一项悬赏。我收到了一些好的和有帮助的答案,但没有什么让我感到舒服“接受”。请记住,我正在寻找一个漂亮干净的方法来使决定Index视图上是否显示“Edit”链接的业务逻辑与决定是否授权对Products/Edit/1进行请求的位置相同。我希望将操作方法中的污染降到最低。理想情况下,我正在寻找一个基于属性的解决方案,但我知道这可能是不可能的。