PHP用户类(登录/注销/注册)

时间:2011-01-16 18:03:01

标签: php oop class login

开始尝试构建类,我开始将用户注册/登录转换为单个类。想要在走得太远之前停下来寻求反馈。

class UserService
{
    private $_email;
    private $_password;

    public function login($email, $password)
    {
        $this->_email = mysql_real_escape_string($email);
        $this->_password = mysql_real_escape_string($password);

        $user_id = $this->_checkCredentials();
        if($user_id){
            $_SESSION['user_id'] = $user_id;
            return $user_id;
        }
        return false;
    }

    protected function _checkCredentials()
    {
        $query = "SELECT *
                    FROM users
                    WHERE email = '$this->_email'";
        $result = mysql_query($query);
        if(!empty($result)){
            $user = mysql_fetch_assoc($result);
            $submitted_pass = sha1($user['salt'] . $this->_password);
            if($submitted_pass == $user['password']){
                return $user['id'];
            }
        }
        return false;
    }   
}

我所遇到的与我班级有关的一个问题是:我应该像这样建立它:

$User = new UserService();
$User->login($_POST['email'], $_POST['password']);

登录方法自动调用_checkCredentials方法。 或者它应该像:

$User = new UserService();
$UserId = $User->checkCredentials($_POST['email'], $_POST['password']);
$User->login($UserId);

除此之外我还喜欢一些关于如何重组这个的提示,请指出我做错的任何事情!

谢谢你们

4 个答案:

答案 0 :(得分:39)

我认为您的主要想法是将用户处理(会话)与数据库查询分开,这在我看来是件好事。

但是,实际实现情况并非如此,因为login会转义要发送到数据库的数据,即使该方法的其余部分与数据库没有任何关系也是如此。并不是说您的数据库查询取决于要运行的全局资源。虽然我很喜欢,但我也建议您使用PDO。

此外,您的属性$_email$_password位于私有范围内,但受保护方法访问。这可能会导致问题。 属性和方法应具有相同的可见性

现在,我可以看到您的UserService 需要三件事:数据库处理程序,电子邮件和密码。 将它放在构造函数中是有意义的。

我将如何做到这一点:

class UserService
{
    protected $_email;    // using protected so they can be accessed
    protected $_password; // and overidden if necessary

    protected $_db;       // stores the database handler
    protected $_user;     // stores the user data

    public function __construct(PDO $db, $email, $password) 
    {
       $this->_db = $db;
       $this->_email = $email;
       $this->_password = $password;
    }

    public function login()
    {
        $user = $this->_checkCredentials();
        if ($user) {
            $this->_user = $user; // store it so it can be accessed later
            $_SESSION['user_id'] = $user['id'];
            return $user['id'];
        }
        return false;
    }

    protected function _checkCredentials()
    {
        $stmt = $this->_db->prepare('SELECT * FROM users WHERE email=?');
        $stmt->execute(array($this->email));
        if ($stmt->rowCount() > 0) {
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
            $submitted_pass = sha1($user['salt'] . $this->_password);
            if ($submitted_pass == $user['password']) {
                return $user;
            }
        }
        return false;
    }

    public function getUser()
    {
        return $this->_user;
    }
}

然后使用它:

$pdo = new PDO('mysql:dbname=mydb', 'myuser', 'mypass');

$userService = new UserService($pdo, $_POST['email'], $_POST['password']);
if ($user_id = $userService->login()) {
    echo 'Logged it as user id: '.$user_id;
    $userData = $userService->getUser();
    // do stuff
} else {
    echo 'Invalid login';
}

答案 1 :(得分:14)

我之前在stackoverflow上已经说了很多但是我认为你做错了是你再次试图创建一个登录系统(甚至Jeff Atwood agrees和我在一起这可能是不安全的。仅举几个可以go wrong的内容:

  • 您不会通过安全连接(https)进行身份验证,这意味着您的用户名/密码可能会被窃听。
  • 它可能有XSS-hole。
  • 数据库中的passwords aren't stored safe,因为使用了不正确的盐。你不是 不是安全专家,所以我认为你甚至不应该将这些敏感信息存储在你的数据库中!
  • 它有一个CSRF - 洞。

然后我们还没有在您的服务器上创建另一个帐户。你可以而且应该避免这种麻烦,使用一种免费提供的替代品,这些替代品已经过专家的安全漏洞测试:

  • openid => Lightopenid是一个非常容易使用/集成的库。甚至stackoverflow / jeff atwood也在使用它,因为他知道很难正确登录系统。即使您是安全专家。
  • google friend connect。
  • facebook connect。
  • twitter单点登录。

让自己安全地再次设计另一个登录系统的时间,而是使用例如非常简单的lightopenid库,让用户使用google帐户登录。下面的代码段是您运行它所需的唯一代码:

<?php
# Logging in with Google accounts requires setting special identity, so this example shows how to do it.
require 'openid.php';
try {
    $openid = new LightOpenID;
    if(!$openid->mode) {
        if(isset($_GET['login'])) {
            $openid->identity = 'https://www.google.com/accounts/o8/id';
            header('Location: ' . $openid->authUrl());
        }
?>
<form action="?login" method="post">
    <button>Login with Google</button>
</form>
<?php
    } elseif($openid->mode == 'cancel') {
        echo 'User has canceled authentication!';
    } else {
        echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.';
    }
} catch(ErrorException $e) {
    echo $e->getMessage();
}

答案 2 :(得分:1)

这取决于您的设计和更简单的解决方案的概念,以及您希望如何组织代码并使其更简单,更小并使其可以同时维护。

问问自己为什么要拨打checkCredentials,然后拨打login,然后再拨打其他方法。你有充分的理由这样做,这是一个很好的设计,我想用这个来实现。

仅调用login,并执行每次登录操作都更加简单和优雅。在这样做的同时赋予它另一个名称更容易理解,也更易于维护。

如果你问我,我会使用一个构造函数。

答案 3 :(得分:0)

答案实际上取决于其他体系结构注意事项,例如checkCredentials()方法是否需要在其他系统元素的类范围之外可用。如果其唯一目的是通过login()方法调用,请考虑将两者合并为一个方法。

我可能提出的另一个建议是在命名方法中使用动词,这意味着“登录”可能过于笼统,无法乍看之下理解其效果。

相关问题