Пользовательский класс PHP (вход / выход из системы / регистрация) - PullRequest
26 голосов
/ 16 января 2011

Начал экспериментировать со сборкой классов, и я начал с преобразования моей регистрации / входа в систему в один класс.Хотел остановиться и попросить обратную связь, прежде чем зайти слишком далеко.

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']);
* 1006»* Где метод login вызывает метод _checkCredentials автоматически.Или это должно быть построено как:
$User = new UserService();
$UserId = $User->checkCredentials($_POST['email'], $_POST['password']);
$User->login($UserId);

Кроме того, мне нравятся некоторые советы о том, как это реструктурировать, и, пожалуйста, укажите на все, что я делаю неправильно!

спасибо, ребята

Ответы [ 4 ]

39 голосов
/ 16 января 2011

Я думаю, что ваша основная идея состояла в том, чтобы отделить обработку пользователя (сеанс) от запроса к базе данных , что, на мой взгляд, хорошо.

Однако это не такс вашей реальной реализацией, потому что 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';
}
14 голосов
/ 16 января 2011

Я уже много раз говорил об этом на stackoverflow, но я думаю, что вы делаете неправильно, что вы снова пытаетесь создать систему входа в систему (даже Джефф Этвуд соглашается с я на этом), который, вероятно, будет небезопасным. Просто назвать несколько вещей, которые могут пойти не так :

  • Вы не выполняете аутентификацию через безопасное соединение (https), что означает, что ваше имя пользователя / пароль могут быть прослушаны с провода.
  • Может иметь XSS-дыру.
  • Пароли не хранятся в безопасности в базе данных из-за неправильного использования соли. Ты рад я не специалист по безопасности, поэтому я не думаю, что вам вообще следует хранить такую ​​конфиденциальную информацию в вашей базе данных!
  • Имеет CSRF -отверстие.

Тогда возникает раздражение, что нам еще предстоит создать еще одну учетную запись на вашем сервере. Вы можете и должны избежать этой проблемы, используя одну из свободно доступных альтернатив, протестированных экспертами на предмет уязвимостей:

  • openid => Lightopenid - это действительно простая библиотека для использования / интеграции. Даже stackoverflow / jeff atwood использует его, потому что он знает, как трудно правильно настроить систему входа в систему. Даже если вы эксперт по безопасности.
  • google friend connect.
  • фейсбук коннект.
  • твиттер единый вход.

Так что защитите себя от времени, чтобы снова разработать другую систему входа в систему и вместо этого использовать, например, действительно простую библиотеку 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();
}
1 голос
/ 16 января 2011

Это зависит от вашего дизайна и концепции более простого решения, а также от того, как вы хотите упорядочить свой код, сделать его проще, меньше по размеру и одновременно поддерживать его в обслуживании.

Спросите себя, почему вы вызываете checkCredentials, а затем вызываете login, а затем, возможно, какой-то другой метод. У вас есть веская причина для этого, хороший ли это дизайн, чего я хочу достичь с помощью этого?

Вызов просто login и выполнение каждой операции входа в систему намного проще и элегантнее. Если дать ему другое имя, то это будет гораздо более понятным и более понятным.

Если вы спросите меня, я бы использовал конструктор.

0 голосов
/ 16 января 2011

Ответ на самом деле зависит от других архитектурных соображений, таких как то, должен ли метод checkCredentials () быть доступным вне области класса для других системных элементов.Если его единственная цель - вызываться методом login (), рассмотрите возможность объединения двух в один метод.

Еще одна рекомендация, которую я мог бы использовать, - это использовать глаголы в методах именования, что означает, что «логин» может быть слишком общим для понимания его последствий на первый взгляд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...