1

我有一个 LoginController 类,如:

class LoginController
{
    protected $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
    }

    public function login($username, $password)
    {
        //Query the database, and get back the password_hash, salt, and user id.
        //Hash the incoming password with the salt.
        //Compare the two hashes.
        //return the logged in user object.
    }
}

因此,在从数据库中检索 password_hash 和 salt 之后,我需要对传入的密码进行哈希处理。在我看来,这似乎是应该由另一个组件处理的第二个责任。也许是一个 PasswordHashingComponent(或者如果你有一个更好的名字)。

abstract class PasswordHashingComponentAbstract
{
    public abstract function hash($data, $salt = "");

    public function verify($hash, $data, $salt = "")
    {
        return $this->hash($data, $salt) == $hash;
    }
}

class MD5PasswordHashingComponent extends PasswordHashingComponentAbstract
{
    public function hash($data, $salt = "")
    {
        return md5($data . md5($salt));
    }
}

class SHA1PasswordHashingComponent extends PasswordHashingComponentAbstract
{
    public function hash($data, $salt = "")
    {
        return sha1($data . sha1($salt));
    }
}

然后我可以依赖注入到 LoginController 中:

public function __construct(PDO $db, PasswordHashingComponentAbstract $passwordhasher);

$loginController = new LoginController($db, new SHA1PasswordHashingComponent());
$loginController->login("username", "password");

所以我的问题是这是否是矫枉过正。

如果我的 LoginController 同时处理登录以及如何对密码进行哈希和比较,这是否是严重的 SRP 违规?

在我看来,这个类可能会改变有两个原因:

  1. 如果处理登录的方式发生变化。
  2. 如果密码被散列和比较的方式发生了变化。

那么,这不违反 SRP 吗?有没有更好的方法可以处理抽象出密码散列部分?

谢谢!

4

1 回答 1

0

如果一个类有更多的功能,那么它就违反了 SRP,并且可以预期其中一些功能会相互独立地发生变化,并且这种变化对类的维护很重要。这个类执行几个功能:

  1. 从服务器端获取数据,

  2. 从客户端获取数据,

  3. 比较和验证来自客户端和服务器端的数据

  4. 向服务器端返回一个结论,并且

  5. 向客户端返回结论。

ad 1. 该数据可以由不同的数据集组成。例如,它可能包含密码、盐、用户名、安全问题和答案。

ad 2. 该数据可以由多条信息组成。可能会随着时间的推移而改变并从不同的来源(验证码和用户数据)获取。它可能不仅仅是用户名和密码。

客户端和服务器端会有一些共同的信息。该信息用于对用户进行身份验证。

ad 3.比较方法用于客户端和服务器端的不同数据集。

ad 4. 您可能决定在登录成功或不成功后在服务器端执行某些操作,例如存储某个帐户或 IP 地址的不成功登录次数,或者您可能会更改存储的盐和密码。

ad 5. 成功与否,客户端必须得到一个消息作为回报。

1、2和3一起变化,1和4可能有共同的连接,2和5有共同的功能连接。3 必须知道来自服务器的数据集和来自客户端的数据集。尽管数据集一起变化,但用于比较的方法可能有所不同。因此,数据集的变化与比较方法之间存在 1:N 的基数。这应该使用接口来实现。

函数“返回服务器端”和“返回客户端”的变化都独立于任何其他实现。服务器端不必有任何实现,建议对客户端的反馈独立于验证过程本身。如果将结论返回给服务器端有一个实现,那么它将很可能与从服务器端检索数据共享连接和有关存储位置的信息。这种重叠应该在设计中回归。到目前为止,您还没有实现将结论返回到服务器端,但这可能非常有用。您可以考虑登录后在服务器端执行的后续步骤: 1. 您可能希望存储某人尝试使用同一台计算机或相同用户名等登录的次数。 2. 每次成功登录(次数)后,您可以更改散列密码和 salt。那时你的记忆中是否有正确的密码。(顺便说一句:总是使用盐。我见过一个应用程序,其中人们被赋予了一个标准化的默认密码,并且管理员知道他们头脑中的哈希密码。他们可以使用没有更改他们的人的密码登录初始登录后的密码。) 3. 您可以将用户的首选项添加到新会话中。4. ... 这一步可能相当复杂,并且会影响对客户的反馈(您已登录失败超过 3 次...

在 UML 类图中,它看起来像这样:

类图 LoginController

我将 LoginController 和服务器端类之间的关系描述为聚合,以表明 LoginController 根据使用的数据模型或其他配置数据来决定使用哪个实现。

于 2013-06-15T09:37:47.477 回答