寻找一种干净,安全的方式,仅允许某些用户(按角色)编辑视图中的某些字段。这里的关键词是编辑,因为只显示/隐藏视图的一部分(按钮,链接等)是一回事,而另一件事是如何处理“受保护”字段,因为它们是被张贴回控制器。
例如,我可以这样做:
查看模型
public class CustomerEditViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
// Other properties...
public string Email { get; set; }
public string Phone { get; set; }
public bool CanEditContactInfo { get; set; }
}
查看
@Html.EditorFor(m => m.FirstName)
@Html.EditorFor(m => m.LastName)
@if (Model.CanEditContactInfo)
{
@Html.EditorFor(m => m.Email)
@Html.EditorFor(m => m.Phone)
}
控制器
[HttpGet]
public ActionResult Edit(int id)
{
var customer = _customerService.GetCustomerById(id);
var model = Mapper.Map<CustomerEditViewModel>(customer);
model.CanEditContactInfo = User.IsInRole("Admin");
return View(model);
}
[HttpPost]
public ActionResult Edit(CustomerEditViewModel model)
{
var customer = _customerRepo.GetCustomerById(model.Id);
Mapper.Map(model, customer);
_customerService.UpdateCustomer(customer);
// return to Index view
}
但问题是,当非管理员用户正在编辑客户时,字段Email
和Phone
永远不会在视图上呈现,因此当表单被回发时它们将为NULL到控制器,并将使用NULL进一步覆盖数据库中的实际值。
我可以这样解决这个问题:
@Html.EditorFor(m => m.FirstName)
@Html.EditorFor(m => m.LastName)
@if (Model.CanEditContactInfo)
{
@Html.EditorFor(m => m.Email)
@Html.EditorFor(m => m.Phone)
}
else
{
@Html.HiddenFor(m => m.Email)
@Html.HiddenFor(m => m.Phone)
}
即使非管理员用户正在编辑记录,这也会预先设定原始的电子邮件/电话值,但问题是,电子邮件/电话字段在呈现的HTML中可用作隐藏字段,并且很容易被操作回帖之前的浏览器工具。
我确实有一些想法,但他们变得一团糟。所以我想知道这样的事情可能会有哪些成功的方法。
答案 0 :(得分:2)
安全编码的第一个经验法则是客户端输入不可信,这意味着您必须在服务器端应用检查和验证。 在您的情况下,HttpPost控制器操作应该再次检查用户角色。如果用户无权更新所有属性,那么您可以:
1-再次加载原始数据并覆盖可以使用用户输入更新的属性,然后保存此更新副本:
[HttpPost]
public ActionResult Edit(CustomerEditViewModel model)
{
var customer = _customerRepo.GetCustomerById(model.Id);
// Check the current user role.
If (!User.IsInRole("Admin"))
{
customer.FirstName = model.FirstName;
customer.LastName = model.LastName;
}
else
{
Mapper.Map(model, customer);
}
_customerService.UpdateCustomer(customer);
// return to Index view
}
2-创建另一种映射方法,忽略无法从用户输入更新的属性,并根据用户权限调用正确的映射器
public static Customer ToEntity(this CustomerEditViewModel model, Customer destination)
{
return Mapper.CreateMap<CustomerEditViewModel, Customer>()
.ForMember(dest => dest.Email, opt => opt.Ignore()
.ForMember(dest => dest.Phone, opt => opt.Ignore()
);
}