PHP 提供了多種用戶認證方式,其中 HTTP 認證和表單驗證是最常見的兩種。本文深入解析這兩種認證機制的原理、實現方法及注意事項。
PHP 的 HTTP 認證僅在 Apache 模塊模式下有效(CGI 模式不支持)。通過 header() 發送 WWW-Authenticate 標頭,瀏覽器會彈出登錄對話框。用戶提交後,PHP 通過預定義變量獲取憑證:
$_SERVER['PHP_AUTH_USER'] - 用戶名$_SERVER['PHP_AUTH_PW'] - 密碼$_SERVER['AUTH_TYPE'] - 認證類型支持 Basic 和 Digest(PHP 5.1.0+)兩種認證方式。
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo '用戶取消登錄';
exit;
} else {
echo "<p>你好,{$_SERVER['PHP_AUTH_USER']}</p>";
echo "<p>你的密碼是:{$_SERVER['PHP_AUTH_PW']}</p>";
}
?>
Digest 認證更安全,密碼不會明文傳輸:
<?php
$realm = 'Restricted area';
$users = array('admin' => 'mypass', 'guest' => 'guest');
if (!isset($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="' . $realm .
'" qop="auth" nonce="' . uniqid() . '" opaque="' . md5($realm) . '"');
die('用戶取消登錄');
}
// 解析 PHP_AUTH_DIGEST
preg_match('/username="(?P<username>.*)",.*realm="(?P<realm>.*)",.*nonce="(?P<nonce>.*)",.*uri="(?P<uri>.*)",.*response="(?P<response>.*)",.*opaque="(?P<opaque>.*)",.*qop=(?P<qop>.*),.*nc=(?P<nc>.*),.*cnonce="(?P<cnonce>.*)"/',
$_SERVER['PHP_AUTH_DIGEST'], $digest);
if (!isset($users[$digest['username']])) die('無效用戶名!');
// 驗證響應
$A1 = md5($digest['username'] . ':' . $realm . ':' . $users[$digest['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $digest['uri']);
$valid_response = md5($A1 . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $A2);
if ($digest['response'] != $valid_response) die('憑證錯誤!');
echo '登錄成功:' . $digest['username'];
?>
WWW-Authenticate 再發送 HTTP/1.0 401Basic 的 B 必須大寫,realm 值必須用雙引號$_SERVER['REMOTE_USER']<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "請輸入有效的登錄信息\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
} else {
echo "<p>歡迎:{$_SERVER['PHP_AUTH_USER']}<br />";
echo "舊認證:{$_REQUEST['OldAuth']}</p>";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>";
echo "<input type='hidden' name='SeenBefore' value='1' />";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />";
echo "<input type='submit' value='重新認證' />";
echo "</form>";
}
?>
當無法使用 HTTP 認證時,可使用表單驗證:
<?php
if ($_POST['Submit'] == "提交") {
$password = $_POST['password'];
$cpassword = $_POST['cpassword'];
if (empty($password) || empty($cpassword)) {
die("密碼不可為空!");
} elseif (strlen($password) < 5 || strlen($password) > 15) {
die("密碼長度需在 5-15 位之間");
} elseif (strlen($password) !== strlen($cpassword)) {
die("兩次輸入長度不一致!");
} elseif ($password !== $cpassword) {
die("兩次密碼不匹配!");
} else {
// 密碼驗證通過,顯示掩碼
for ($i = 0; $i < strlen($password); $i++) echo "*";
}
}
?>
<html>
<head><title>表單驗證</title></head>
<body>
<form method="post" action="">
請輸入密碼:<input type="password" name="password"><br>
確認密碼:<input type="password" name="cpassword"><br>
<input type="submit" name="Submit" value="提交">
</form>
</body>
</html>
實際項目中應結合數據庫驗證:
<?php
if (!isset($PHP_AUTH_USER)) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo '需要授權';
exit;
} elseif (($PHP_AUTH_USER != "admin") || ($PHP_AUTH_PW != "123")) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo '授權失敗';
exit;
} else {
echo "<p>授權成功!</p>";
}
?>
建議生產環境使用加密存儲密碼(如 password_hash()),並採用 Prepared Statements 防止 SQL 注入。