如何在回发后维护模型值而不公开它们

本文关键字:模型 维护 | 更新日期: 2023-09-27 18:20:58

我正在使用UserProfile编辑页面,在该页面上只能编辑UserProfile模型的字段子集。许多字段仅对具有特殊角色的用户可编辑,字段UserName当然是不可编辑和隐藏的。

现在我正在考虑为所有不需要用户编辑的字段添加隐藏字段,并将我的模型装饰成这样:

[Table("UserProfile")]
public partial class UserProfile
{
    public UserProfile()
    {
        webpages_Roles = new HashSet<Role>();
    }
    [Key]
    public int UserId { get; set; }
    [Required]
    [StringLength(56)]
    [Display(Name="Email")]
    [Editable(false)] // is this the way to go?
    public string UserName { get; set; }
    [Required]
    [Display(Name = "First name")]
    [StringLength(256)]
    public string FirstName { get; set; }
    [Editable(false)] // is this the way to go?
    public bool SomeSetting { get; set; }
    // ... more properties are unimportant for this example
}

其他相关代码位:

    //
    // GET: /Account/Profile
    public ActionResult UserProfile()
    {
        var userProfile = db.UserProfiles.Find(WebSecurity.CurrentUserId);
        return View(userProfile);
    }
    //
    // POST: /Account/Profile
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult UserProfile(UserProfile model)
    {
        // if I dont include UserName, validation will fail since it 
        // is now null and the field is [Required]
        if (ModelState.IsValid)
        {
            // if I dont include hidden fields, UserId, UserName
            // and SomeSetting will be null here
            db.Entry(model).State = EntityState.Modified;
            db.SaveChanges();
        }
        return View(model);
    }

相关视图代码:

@Html.HiddenFor(m => m.UserId)
@Html.HiddenFor(m => m.UserName)
@Html.HiddenFor(m => m.SomeSetting)

但是,我担心通过隐藏的输入暴露这些字段。聪明或恶意的用户无论如何都不能编辑它们吗?我知道我必须包括它们,否则回发后属性将为null。有人能启发我吗?

如何在回发后维护模型值而不公开它们

您可以在模型上生成一个散列作为属性,该属性是串联字符串中所有值的散列,例如:

将新属性添加到ViewModel中(如果这也是数据库对象,则可以使用[NotMapped]注释)

public string SecurityHash {get;set;}

在助手(或控制器)中创建一个简单的哈希函数:

public string CalculateMD5Hash(string input)
{
    // step 1, calculate MD5 hash from input
    MD5 md5 = System.Security.Cryptography.MD5.Create();
    byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
    byte[] hash = md5.ComputeHash(inputBytes);
    // step 2, convert byte array to hex string
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < hash.Length; i++)
    {
        sb.Append(hash[i].ToString("X2"));
    }
    return sb.ToString();
}

现在在控制器中设置哈希值:

//
// GET: /Account/Profile
public ActionResult UserProfile()
{
    var userProfile = db.UserProfiles.Find(WebSecurity.CurrentUserId);
    userProfile.SecurityHash = MyHashHelper.CalculateMD5Hash(userProfile.UserID + userProfile.UserName + userProfile.SomeSetting)
    return View(userProfile);
}

然后在你的视图中,保持你的哈希值:

@Html.HiddenFor(m => m.UserId)
@Html.HiddenFor(m => m.UserName)
@Html.HiddenFor(m => m.SomeSetting)
@Html.HiddenFor(m => m.SecurityHash)

最后,您可以检查您的值是否被篡改,并在POST后重新散列:

//
// POST: /Account/Profile
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult UserProfile(UserProfile model)
{
    string hashCheckVal = MyHashHelper.CalculateMD5Hash(model.UserID + model.UserName + model.SomeSetting)
    if(string.Compare(hashCheckVal, model.SecurityHash) != 0)
    {
        throw new Exception("tampered with!");
    }
    // if I dont include UserName, validation will fail since it 
    // is now null and the field is [Required]
    if (ModelState.IsValid)
    {
        // if I dont include hidden fields, UserId, UserName
        // and SomeSetting will be null here
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
    }
    return View(model);
}

如果您关心安全性,则应在回发后从数据库中检索实体,然后从提交的模型中更新可编辑属性。为了检索您的模型,我认为可以将UserId保留为隐藏字段:@Html.HiddenFor(m => m.UserId)

我希望这个答案能有所帮助。