Apichart Nakarungutti
The Cake is a Lie
คราวก่อน เขียนถึงระบบสมาชิกส่วนแรกซึ่งก็คือ การสมัครสมาชิก คราวนี้มาดูการ 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()
ในการเตรียมข้อมูล ตราบใดที่ไม่มีบั๊กในฟังชั่นนี้ ฐานข้อมูลของเรายังปลอดภัยอยู่