将登录名和密码哈希合并到同一个类中会违反SRP吗?

时间:2013-01-25 21:45:03

标签: php design-patterns login single-responsibility-principle

我有一个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. 如果对密码进行哈希处理并比较更改。
  3. 那么,这不违反SRP吗?有没有更好的方法来处理抽象密码散列部分?

    谢谢!

1 个答案:

答案 0 :(得分:0)

如果某个类具有更多功能,则会违反SRP,并且可以预期这些功能中的某些功能会彼此独立地发生变化,并且这些变化对于维护类很重要。 该类执行几个函数:

  1. 从服务器端获取数据

  2. 从客户端获取数据

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

  4. 向服务器端返回结论,

  5. 向客户端返回结论。

  6. ad 1.此数据可以包含不同的数据集。它可能包含密码,盐,用户名,安全问题和答案。

    ad 2.此数据可包含多条信息。可能会更改加班并从不同来源(CAPTCHA和用户数据)获取。它可能只是一个用户名和密码。

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

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

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

    ad 5.成功与否,客户将不得不收到回复消息。

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

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

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

    Class diagram LoginController

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