web-dev-qa-db-ja.com

PHPユーザークラス(ログイン/ログアウト/サインアップ)

クラスの構築の実験を開始し、ユーザー登録/ログインを単一のクラスに変換することから始めました。立ち去る前に立ち止まってフィードバックを求めたかった。

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;
    }   
}

私のクラスに関連する質問の1つは次のとおりです。

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

ログインメソッドが_checkCredentialsメソッドを自動的に呼び出す場所。または、次のように構築する必要があります。

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

それ以外に、これを再構築する方法に関するいくつかのヒントが大好きで、間違っていることを指摘してください!

みんなありがとう

26
Nouman

あなたの主なアイデアはデータベースクエリからユーザー処理(セッション)を分離するだったと思います。これは私の意見では良いことです。

ただし、メソッドの残りの部分がデータベースとは関係がない場合でも、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';
}
39
netcoder

私は以前にstackoverflowでこれをたくさん言いましたが、あなたが間違っていると思うのは、あなた再びがログインシステムを作成しようとしているということです(ジェフさえも) Atwood agrees これで私と)これはおそらく安全ではないでしょう。 間違っている

  • 安全な接続(https)を介して認証を行うことはありません。つまり、ユーザー名/パスワードが盗聴される可能性があります。
  • XSSホールがある可能性があります。
  • パスワードは安全に保存されません saltの誤った使用のためにデータベースに保存されます。あなたはセキュリティの専門家ではないので、とにかくそのような機密情報をデータベースに保存する必要さえないと思います!
  • [〜#〜] csrf [〜#〜] -holeがあります。

次に、サーバー上に別のアカウントを作成する必要があります。専門家によってセキュリティの脆弱性についてテストされている、無料で利用可能な代替手段の1つを使用することで、この面倒を回避できます。

  • openid => Lightopenid は、使用/統合が非常に簡単なライブラリです。 stackoverflow/jeff atwoodでさえ、ログインシステムを正しく取得するのが難しいことを知っているため、それを使用しています。あなたがセキュリティの専門家であっても。
  • google friend connect。
  • facebookの接続。
  • 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();
}
14
Alfred

それは、設計とより単純なソリューションの概念、およびコードを整理し、よりシンプルで小さく保ち、同時に保守可能にする方法に依存します。

checkCredentialsを呼び出してからloginを呼び出してから、他のメソッドを呼び出す理由を考えてください。そうする理由はありますか、これは良いデザインですか、これで何を達成したいのですか?.

loginを呼び出して、すべてのログイン操作を実行することは、はるかに単純でエレガントです。そうする間に別の名前を付けると、はるかに理解しやすくなり、保守しやすくなります。

あなたが私に尋ねるなら、私はコンストラクタを使用します。

1
Secko

答えは、checkCredentials()メソッドを他のシステム要素のクラススコープ外で使用可能にする必要があるかどうかなど、他のアーキテクチャ上の考慮事項に依存します。その唯一の目的がlogin()メソッドによって呼び出される場合、2つを1つのメソッドに結合することを検討してください。

もう1つの推奨事項は、命名方法に動詞を使用することです。つまり、「ログイン」は一見しただけではその効果を理解するには一般的すぎる可能性があります。

0