【问题标题】:ASP.NET Core model binding on Controller with a private property getter (for Password)使用私有属性获取器(用于密码)在控制器上绑定 ASP.NET Core 模型
【发布时间】:2018-03-06 17:25:13
【问题描述】:

我有这个简单的用户类,对于 Password 属性,我想将 getter 设为私有(仅在内部使用 - 公开一个公共的 VerifyPassword 方法,该方法将在内部执行验证并返回一个布尔值)

现在问题来自我的控制器中路由上的模型绑定。当我尝试使用 asp-for 标签助手指向 Model.Password 时,它告诉我“密码不能在这种情况下使用,因为 get-accessor 不可用”

我是初学者。我认为将 getter 设为私有是保护它不被暴露的明智之举。但是现在我不能在模型绑定中使用它。

所以有几个问题: 这是保护密码的正确方法吗? 这是使用模型绑定的正确方法吗? 有什么可以改进的建议吗?

这是模型:

    using System;

namespace user_signup.Models {
    public class User
    {
        private string username;
        private string email;
        private string password;

        public string Username { 
            get => username;
            set {
                if (value.Length < 5) {
                    throw new ArgumentException("Username is too short.");
                }
                if (value.Length > 15) {
                    throw new ArgumentException("Username is too long.");
                }
                username = value;
            }
        }
        public string Email {
            get => email;
            set {
                // email validation here
                email = value; 
            }
        }

        public string Password {
            // the getter is private (only accessable internally)
            private get => password;
            set {
                if (value.Length < 5) {
                    throw new ArgumentException("Password is too short.");
                } 
                if (value.Length > 15) {
                    throw new ArgumentException("Password is too long.");
                }

                password = value;
            }
        }

        // public means of verifying a password (the password itself is never exposed)
        public bool VerifyPassword(string pass) => Password.Equals(pass);


        // counter of all users that have been instantiated (prevents overlapping IDs)
        // static so it is at the Class level (can be counted globally outside of all User instances)
        private static int NextId = 0;

        // can only be set internally (private setter)
        // defaults to the CURRENT VALUE of NextId and THEN increments NextId for the next instantiation
        public int UserId { get; } = NextId++;

        // can only be set internally (private setter)
        // defaults to current DateTime in string format
        public string CreateDate { get; private set; } = DateTime.Now.ToString(); 

        // the model will be populated from form data sent in a Request
        public User() {}

        // for creating a user by other means (manually)
        public User(string u, string e, string p) {
            Username = u;
            Email = e;
            Password = p;
        }

        public override string ToString() {
            return String.Format(
                "UserId: {0}\nUsername: {1}\nEmail: {2}\n\n",
                UserId,
                Username,
                Email
            );

        }
    }
}

这里是控制器(特别是 Add.cshtml GET 和 POST 处理程序)

    [HttpGet]
    public IActionResult Add() {
        VModel.User = new User();
        return View(VModel);
    }

    [HttpPost]
    public IActionResult Add(User user, string verify) {
        VModel.User = user;
        VModel.Users = Users.GetUsers();
        if (!user.VerifyPassword(verify)) {
            VModel.Errors["verify"] = "Verify password failed. Passwords do not match.";
            return View(VModel);
        }

        return View("Index", VModel);

    }

最后是 Add.cshtml 视图

@using user_signup.Controllers
@model UserController.ViewModel

<!DOCTYPE html>

<html>
<head>
    <title>title</title>
</head>
<body>
<div>
    <form  asp-controller="User" asp-action="Add" method="post">
        <label>Username: </label>
        <input type="text" asp-for="@Model.User.Username" required/>

        <label>Email: </label>
        <input type="text" asp-for="@Model.User.Email"/>

        <label>Password: </label>
        <input type="password" asp-for="@Model.User.Password" required/>

        <label>Verify Password: </label>
        <input type="password" name="verify" required/>

        <input type="submit" value="Sign Up"/>

    </form>
</div>
</body>
</html>

【问题讨论】:

  • 公开即可。由于上述原因,没有必要将其设为私有。阅读以下内容,了解为什么要将属性设为私有:stackoverflow.com/questions/3847832/…
  • @RichardMc 这是在谈论私人二传手。我有一个私人 getter,所以密码只能在课堂内部读取
  • 我不是要争论。我只是想知道为什么这不是一个好主意,所以我可以学习。我的直觉是,将 getter 设为私有对于密码属性会更安全。你能纠正我的直觉,这样我就可以学习了
  • 我想你可能对私有的目的有点困惑。您说得对,它限制了对其当前类的访问,但除非您不信任自己的代码,否则它不会使密码属性更安全。密码安全与您存储和传输密码的方式有关。 tchelidze 下面概述的内容描述了如何正确保护密码。那里有很多,但值得了解。

标签: c# razor asp.net-core-mvc private model-binding


【解决方案1】:
  • 不要将密码以纯文本形式存储在数据库中
  • 使用ASP.NET Identity 管理身份验证
  • 不要在视图中直接使用域实体(本例中为User),而是使用ViewModel
  • 用户Fluent Validation 用于验证用户输入

【讨论】:

  • 感谢您提供的信息。这些是我接下来要采取的很好的步骤。现在我只是在练习将数据绑定到模型。不幸的是,ms 文档非常复杂和令人困惑
  • 对于模型绑定,private 属性没有意义,ViewModel 可以将数据从浏览器传输到控制器。
  • 您能否详细说明为什么使用视图模型而不是实体?我正在遵循的教程将用户作为路由处理程序的参数,以便可以发生“'表单数据的模型绑定”
  • @vampiire 当然,看看this
  • 我看到的另一种选择是只在控制器中接收表单输入,并在处理程序主体中手动填充模型的句柄。这似乎是更正确的方法吗?
猜你喜欢
  • 2020-01-28
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 2018-01-02
  • 2018-11-30
  • 1970-01-01
  • 2022-06-11
相关资源
最近更新 更多