ระบบสมาชิก - ยืนยันตัวตน

คราวก่อน เขียนถึงระบบสมาชิกส่วนแรกซึ่งก็คือ การสมัครสมาชิก คราวนี้มาดูการ log in กับ log out กันบ้าง เริ่มต้นจากสร้างแบบฟอร์มสำหรับ log in กันก่อน

    <!-- FILE: login.html -->
    <form id="login" method="post" action="login.php">
        <label for="username">User:</label>
        <input type="text" id="username" name="username" />
        <label for="password">Pass:</label>
        <input type="password" id="password" name="password" />

        <input type="submit" id="login" name="login" value="Log in" />
    </form>

แบบฟอร์มก็แบบเดียวกับที่พบได้ตามเว็บไซต์ทั่วไป ส่วน .php ที่ใช้ในการ log in จะเริ่มต้นด้วยการตรวจสอบเบื้องต้นก่อนว่า ผู้ใช้ลืมกรอกข้อมูล หรือเปล่า

    <?php
    // FILE: login.php

    $error = false;

    // get log in information via post
    $u_name = $_POST['username'];
    $u_pass = $_POST['password'];

    // check empty input
    if (empty($u_name) || empty($u_pass)) {
     $error = true;
    }

    if (!$error) {
        // everything fine, check user info in database
    }
    else {
        // empty username or password, tell the user
        echo 'Invalid username or password.';
    }

ถ้าทุกอย่างไม่มีปัญหาอะไร ก็มาตรวจสอบบัญชีผู้ใช้ในฐานข้อมูล โดยใช้การเชื่อมต่อผ่าน PDO เหมือนเดิม ซึ่งตอนที่ตรวจสอบรหัสผ่านนั้น ในตอนสมัครสมาชิก เราใช้การ hash และ salt ในการเก็บข้อมูลของรหัสผ่าน ในขั้นตอน log in เราก็จะต้องแปลงรหัสผ่านให้กลายเป็น hash ที่ถูกต้องด้วย

    <?php
    // FILE: login.php
    .
    .
    .

    if (!$error) {
        // everything fine, check user info in database

        // db info
        $db_host = 'localhost';
        $db_name = 'keancode';
        $db_user = 'keanuser';
        $db_pass = 'keanpass';

        // connect to database
        try {
            $conn = new PDO("mysql:host=$db_host; dbname=$db_name", $db_user, $db_pass);
            
            $conn->exec("SET CHARACTER SET utf8");
            
            // prepare checking
            $user = array(
                'name' => $u_name,
                // we use this salt, we must check with this salt
                'pass' => sha1('NaCl' . $u_pass . md5($u_pass)),
            );

            // prepare sql
            $result = $conn->prepare("SELECT * FROM members WHERE username=:name AND password=:pass");

            // filled prepared sql statement with $user
            $result->execute($user);

            // row count must == 1
            if ($result !== false && $result->rowCount() == 1) {

                // redirect to other page (member.php)
                // warning: there must no another output before this, otherwise it might failed
                header('Location: member.php');
            }
            else {
                echo 'Invalid username or password.';
            }
        }
        catch (PDOException $e) {
           echo $e->getMessage();
        }
    }

ตอนนี้เราสามารถ log in เข้ามาได้แล้ว แต่ผู้ใช้ต้อง log in ใหม่ทุกครั้งเวลาเปลี่ยนหน้า อันนี้ต้องใช้ session เข้ามาช่วยในการจดจำผู้ใช้

เริ่มต้นด้วยการเพิ่ม session_start() ไว้ในส่วนบนสุดของ .php

    <?php
    //FILE: login.php

    session_start();

    $error = false;

    .
    .
    .

ต่อมาก็เพิ่มโค้ดให้เก็บค่าลง session เข้าไปก่อนที่จะ redirect ไปยังหน้าอื่น

    <?php
    // FILE: login.php
    .
    .
    .

    // row count must == 1
    if ($result !== false && $result->rowCount() == 1) {
        // fetch data
        $data = $result->fetch();

        // saved to session
        SESSION['username'] = $data['username'];
        SESSION['email'] = $data['email'];

        // redirect to other page (member.php)
        // warning: there must no another output before this, otherwise it might failed
        header('Location: member.php');
    }
    else {
        echo 'Invalid username or password.';
    }

    .
    .
    .

และเพื่อไม่ให้มีการ log in ซ้ำซ้อน ซึ่งอาจจะนำปัญหามาให้ ก็ให้ตรวจสอบก่อนว่ามีการ log in อยู่แล้วหรือไม่เสียก่อน ในตอนเริ่มแรก

    <?php
    //FILE: login.php

    session_start();

    // if already logged in redirect
    if (isset($_SESSION['username'])) {
        header('Location: member.php');
    }

    $error = false;

    .
    .
    .

ส่วนในหน้าอื่น ๆ ที่จำเป็นต้อง log in ก่อน เพื่อแสดงข้อมูลออกมาก็ให้ตรวจสอบค่า $_SESSION['username'] ดูว่ามีการ log in หรือไม่ ถ้ามีก็ดึงข้อมูลมาตามปกติ แต่ถ้าไม่มีจะบังคับให้ log in เสียก่อน หรือแสดงข้อมูลที่แตกต่างกันออกไป อันนี้ก็แล้วแต่จะออกแบบ

    <?php
    // FILE: member.php

    // always start with this
    session_start();

    // check log in status
    if (isset($_SESSION['username'])) {
        // do anything you want
        echo 'Hello ' . $_SESSION['username'] . ' (' . $_SESSION['email'] . ')';
        echo '\r\n';
        echo '<a href="logout.php">Log out</a>';
    }
    else {
        echo 'Please log in!.';
    }

ส่วนการ logout นั้นทำได้ง่าย ๆ ด้วยคำสั่ง session_destroy() ทุกอย่างก็หายไป

    <?php
    // FILE: logout.php

    session_start();
    session_destroy();

    echo 'Logged out';

ทั้งหมดนี้เป็นเพียง โครงร่างคร่าว ๆ ที่สามารถทำงานได้ ถ้าหากต้องการให้เว็บออกมาสวยงาม จำเป็นต้องใช้การวางเลย์เอ้าท์ รวมทั้งการออกแบบการไหลของข้อมูลเสียใหม่ แต่โดยรวมแล้ว จะมีฟังชั่นที่จำเป็นเพียงเท่านี้

ที่ผ่านมาจะเห็นว่าไม่มีการพูดถึง SQL Injection เลยเพราะว่า เราใช้ฟังชั่น PDO::prepare() ในการเตรียมข้อมูล ตราบใดที่ไม่มีบั๊กในฟังชั่นนี้ ฐานข้อมูลของเรายังปลอดภัยอยู่

blog comments powered by Disqus