傳統登入需跳轉頁面,體驗不佳。結合 AJAX + 驗證碼 + Session,可實現無刷新登入、驗證失敗即時回饋、驗證碼自動更新。本文提供三檔案完整架構:驗證碼產生、前端頁面、後端驗證。
$_SESSION['VCODE']ok 或 no<?php
session_start();
header('Content-Type: image/png');
$width = 44;
$height = 18;
$length = 4;
$img = imagecreate($width, $height);
// 背景
$bg = imagecolorallocate($img, 245, 245, 245);
imagefill($img, 0, 0, $bg);
// 干擾像素
for ($i = 0; $i < 100; $i++) {
$color = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
imagesetpixel($img, rand() % 70, rand() % 30, $color);
}
// 產生 4 位數字
$code = '';
for ($i = 0; $i < $length; $i++) {
$num = rand(1, 9);
$code .= $num;
$color = imagecolorallocate($img, rand(100, 255), rand(0, 100), rand(100, 255));
imagestring($img, 5, 2 + $i * 10, 1, $num, $color);
}
// 存入 Session (關鍵:供 loginCheck.php 比對)
$_SESSION['VCODE'] = $code;
imagepng($img);
imagedestroy($img);
<!DOCTYPE html>
<html>
<head>
<meta charset="gb2312">
<title>管理後台登入</title>
<style>
body { background: url(./images/w2.jpg) center fixed no-repeat; font-family: 微軟正黑體; }
#main { margin-top: 150px; text-align: center; font-size: 14px; }
table { margin: 0 auto; }
input { margin: 0 5px; padding: 5px; }
#vcode { cursor: pointer; vertical-align: middle; }
</style>
<script src="./js/trim.js"></script>
<script>
// 清空驗證碼輸入框
function clearCode() {
document.getElementById('authCode').value = '';
}
// 更新驗證碼圖片
function refreshCode() {
var img = document.getElementById('vcode');
img.src = 'yz.php?nocache=' + Date.now();
}
// 建立 XMLHttpRequest (相容 IE6+)
function createXHR() {
if (window.XMLHttpRequest) return new XMLHttpRequest();
try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
return null;
}
var xhr = createXHR();
// AJAX 登入驗證
function doLogin() {
var user = trim(document.getElementById('username').value);
var pass = trim(document.getElementById('password').value);
var code = trim(document.getElementById('authCode').value);
if (!user || !pass || !code) {
alert('請輸入帳號、密碼、驗證碼!');
return;
}
if (!xhr) {
alert('瀏覽器不支援 AJAX');
return;
}
var params = 'username=' + encodeURIComponent(user) +
'&password=' + encodeURIComponent(pass) +
'&authCode=' + encodeURIComponent(code) +
'&fresh=' + Math.random(); // 破解快取
xhr.open('POST', 'loginCheck.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(params);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var result = xhr.responseText.trim();
if (result === 'ok') {
window.location.href = 'adminCenter.php';
} else {
alert('帳號、密碼或驗證碼錯誤!');
document.getElementById('username').focus();
refreshCode(); // 失敗自動換碼
}
}
};
}
</script>
</head>
<body onload="document.getElementById('username').focus()">
<div id="main">
<table>
<tr>
<td>帳號:</td>
<td><input type="text" id="username"></td>
<td>密碼:</td>
<td><input type="password" id="password"></td>
<td>驗證碼:</td>
<td><input type="text" id="authCode" size="6" maxlength="4" value="驗證碼" onfocus="clearCode()"></td>
<td><img id="vcode" src="yz.php" alt="看不清?點擊換一張" onclick="refreshCode()"></td>
<td><button onclick="doLogin()">登入</button></td>
</tr>
</table>
</div>
</body>
</html>
<?php
session_start();
require_once '../conn/connDB.php'; // 資料庫連線
$username = $_POST['username'] ?? '';
$password = md5($_POST['password'] ?? ''); // 實務請用 password_hash()
$authCode = $_POST['authCode'] ?? '';
$result = 'no';
// 1. 驗證碼比對 (Server 端比對,安全)
if (strtoupper($authCode) === strtoupper($_SESSION['VCODE'] ?? '')) {
// 2. 資料庫查詢
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$res = mysql_query($sql); // 實務請用 PDO + Prepared Statement
if (mysql_num_rows($res) === 1) {
$result = 'ok';
$_SESSION['admin'] = true; // 標記登入狀態
}
}
echo $result;
| 風險點 | 本範例做法 | 生產環境建議 |
|---|---|---|
| SQL 注入 | 直接拼接字串 | PDO + Prepared Statement |
| 密碼儲存 | MD5 (已淘汰) | password_hash() / password_verify() |
| 驗證碼強度 | 4 位純數字 | 英數混合、干擾線、或 reCAPTCHA/hCaptcha |
| 暴力破解 | 無防禦 | IP 限流、帳號鎖定、雙因子認證 |
| CSRF | 無 Token | 表單隱藏 CSRF Token、SameSite Cookie |
| HTTPS | 明文傳輸 | 全站強制 HTTPS |
// index.php 現代化版本
async function doLogin() {
const formData = new FormData(document.getElementById('loginForm'));
formData.append('fresh', Date.now());
try {
const res = await fetch('loginCheck.php', {
method: 'POST',
body: formData,
credentials: 'same-origin' // 攜帶 Session Cookie
});
const result = await res.text();
if (result === 'ok') {
location.href = 'adminCenter.php';
} else {
alert('登入失敗');
refreshCode();
}
} catch (e) {
console.error(e);
alert('網路錯誤');
}
}
AJAX 登入核心流程:前端攔截提交 → 發送帳密碼 → 後端驗證碼比對 → 資料庫查詢 → 回傳結果 → 前端決定跳轉或換碼。本範例展示傳統 XMLHttpRequest 寫法,現代專案建議改用 fetch() + FormData,並務必強化資料庫查詢、密碼儲存、傳輸加密等安全面向。