我将为您提供一个完整的PHP人脸识别认证系统实现方案,这个系统将使用百度AI开放平台提供的人脸识别API。

完整的人脸识别认证系统
系统架构
项目结构:
├── config/
│ └── config.php # 配置文件
├── includes/
│ ├── FaceAuth.php # 人脸认证核心类
│ ├── Database.php # 数据库操作类
│ └── ImageUpload.php # 图片上传处理类
├── uploads/ # 上传图片目录
├── register.php # 人脸注册页面
├── login.php # 人脸登录页面
├── auth_callback.php # 认证回调处理
└── database.sql # 数据库结构
数据库结构
-- database.sql
CREATE DATABASE IF NOT EXISTS face_auth_system;
USE face_auth_system;
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
face_token VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
);
-- 认证记录表
CREATE TABLE auth_logs (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
auth_type ENUM('register', 'login', 'verify') NOT NULL,
result ENUM('success', 'failed') NOT NULL,
score FLOAT,
ip_address VARCHAR(45),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
配置文件
<?php
// config/config.php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_NAME', 'face_auth_system');
define('DB_USER', 'root');
define('DB_PASS', '');
// 百度AI配置
define('BAIDU_APP_ID', '你的App ID');
define('BAIDU_API_KEY', '你的API Key');
define('BAIDU_SECRET_KEY', '你的Secret Key');
define('BAIDU_ACCESS_TOKEN_URL', 'https://aip.baidubce.com/oauth/2.0/token');
define('BAIDU_GROUP_ID', 'your_face_group');
// 系统配置
define('FACE_AUTH_THRESHOLD', 80); // 人脸认证准确率阈值
define('MAX_IMAGE_SIZE', 10 * 1024 * 1024); // 10MB
// 获取百度AI Access Token
function getBaiduAccessToken() {
$url = BAIDU_ACCESS_TOKEN_URL . '?grant_type=client_credentials&client_id=' .
BAIDU_API_KEY . '&client_secret=' . BAIDU_SECRET_KEY;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
return $data['access_token'];
}
?>
核心类 - 人脸认证
<?php
// includes/FaceAuth.php
class FaceAuth {
private $accessToken;
private $appId;
private $apiUrl;
public function __construct() {
$this->appId = BAIDU_APP_ID;
$this->accessToken = getBaiduAccessToken();
$this->apiUrl = 'https://aip.baidubce.com/rest/2.0/face/v3/';
}
/**
* 人脸注册
* @param string $imageBase64 - 人脸图片Base64编码
* @param string $userId - 用户ID
* @return array - 返回face_token等信息
*/
public function faceRegister($imageBase64, $userId) {
$url = $this->apiUrl . 'face/add?access_token=' . $this->accessToken;
$params = [
'image' => $imageBase64,
'image_type' => 'BASE64',
'group_id' => BAIDU_GROUP_ID,
'user_id' => $userId,
'user_info' => 'User registration at ' . date('Y-m-d H:i:s')
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 人脸搜索(认证登录)
* @param string $imageBase64 - 待认证人脸图片
* @return array - 返回匹配结果
*/
public function faceSearch($imageBase64) {
$url = $this->apiUrl . 'search?access_token=' . $this->accessToken;
$params = [
'image' => $imageBase64,
'image_type' => 'BASE64',
'group_id_list' => BAIDU_GROUP_ID,
'quality_control' => 'NORMAL',
'liveness_control' => 'NONE'
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 人脸比对
* @param string $image1 - 第一张人脸图
* @param string $image2 - 第二张人脸图
* @return array - 返回相似度分数
*/
public function faceCompare($image1, $image2) {
$url = $this->apiUrl . 'match?access_token=' . $this->accessToken;
$params = [
[
'image' => $image1,
'image_type' => 'BASE64'
],
[
'image' => $image2,
'image_type' => 'BASE64'
]
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 检测人脸质量
* @param string $imageBase64 - 人脸图片
* @return array - 返回人脸质量评估结果
*/
public function faceDetect($imageBase64) {
$url = $this->apiUrl . 'detect?access_token=' . $this->accessToken;
$params = [
'image' => $imageBase64,
'image_type' => 'BASE64',
'face_field' => 'quality,angle,beauty,age,gender'
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 更新用户人脸
* @param string $imageBase64 - 新的人脸图片
* @param string $faceToken - 原人脸token
* @param string $userId - 用户ID
* @return array
*/
public function faceUpdate($imageBase64, $faceToken, $userId) {
$url = $this->apiUrl . 'face/update?access_token=' . $this->accessToken;
$params = [
'image' => $imageBase64,
'image_type' => 'BASE64',
'group_id' => BAIDU_GROUP_ID,
'user_id' => $userId,
'face_token' => $faceToken
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 删除用户人脸
* @param string $faceToken - 人脸token
* @param string $userId - 用户ID
* @return array
*/
public function faceDelete($faceToken, $userId) {
$url = $this->apiUrl . 'face/delete?access_token=' . $this->accessToken;
$params = [
'face_token' => $faceToken,
'group_id' => BAIDU_GROUP_ID,
'user_id' => $userId
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 获取用户信息
* @param string $userId - 用户ID
* @return array
*/
public function getUserInfo($userId) {
$url = $this->apiUrl . 'user/get?access_token=' . $this->accessToken;
$params = [
'group_id' => BAIDU_GROUP_ID,
'user_id' => $userId
];
$result = $this->curlPost($url, $params);
return $result;
}
/**
* 人脸活体检测
* @param string $imageBase64 - 人脸图片
* @param string $faceFields - 需要返回的属性
* @return array
*/
public function faceLiveness($imageBase64, $faceFields = 'quality,faceliveness') {
$url = $this->apiUrl . 'liveness?access_token=' . $this->accessToken;
$params = [
'image' => $imageBase64,
'image_type' => 'BASE64',
'face_field' => $faceFields
];
$result = $this->curlPost($url, $params);
return $result;
}
private function curlPost($url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
/**
* 验证人脸认证结果
* @param array $result - 百度API返回结果
* @param float $threshold - 准确率阈值
* @return array - 认证结果
*/
public function validateAuthResult($result, $threshold = null) {
if ($threshold === null) {
$threshold = FACE_AUTH_THRESHOLD;
}
if (isset($result['error_code']) && $result['error_code'] !== 0) {
return [
'success' => false,
'error' => $result['error_msg']
];
}
if (isset($result['result']['user_list'][0])) {
$userInfo = $result['result']['user_list'][0];
$score = $userInfo['score'];
if ($score >= $threshold) {
return [
'success' => true,
'score' => $score,
'user_id' => $userInfo['user_id'],
'face_token' => $userInfo['face_token']
];
} else {
return [
'success' => false,
'error' => '认证准确率不足,当前为:' . $score . '%,需要达到' . $threshold . '%以上',
'score' => $score
];
}
}
return [
'success' => false,
'error' => '未找到匹配的人脸信息'
];
}
}
?>
数据库操作类
<?php
// includes/Database.php
class Database {
private $host;
private $dbName;
private $username;
private $password;
private $connection;
private static $instance = null;
private function __construct() {
$this->host = DB_HOST;
$this->dbName = DB_NAME;
$this->username = DB_USER;
$this->password = DB_PASS;
$this->connect();
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Database();
}
return self::$instance;
}
private function connect() {
try {
$dsn = "mysql:host={$this->host};dbname={$this->dbName};charset=utf8mb4";
$this->connection = new PDO($dsn, $this->username, $this->password);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
}
public function getConnection() {
return $this->connection;
}
public function insertUser($username, $email, $faceToken) {
$sql = "INSERT INTO users (username, email, face_token) VALUES (?, ?, ?)";
$stmt = $this->connection->prepare($sql);
$result = $stmt->execute([$username, $email, $faceToken]);
return $result ? $this->connection->lastInsertId() : false;
}
public function getUserByFaceToken($faceToken) {
$sql = "SELECT * FROM users WHERE face_token = ?";
$stmt = $this->connection->prepare($sql);
$stmt->execute([$faceToken]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function getUserByUsername($username) {
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $this->connection->prepare($sql);
$stmt->execute([$username]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function logAuthAttempt($userId, $authType, $result, $score = null) {
$ipAddress = $_SERVER['REMOTE_ADDR'];
$sql = "INSERT INTO auth_logs (user_id, auth_type, result, score, ip_address)
VALUES (?, ?, ?, ?, ?)";
$stmt = $this->connection->prepare($sql);
return $stmt->execute([$userId, $authType, $result, $score, $ipAddress]);
}
}
?>
注册页面
<?php
// register.php
session_start();
require_once 'config/config.php';
require_once 'includes/Database.php';
require_once 'includes/FaceAuth.php';
$db = Database::getInstance()->getConnection();
$faceAuth = new FaceAuth();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 获取表单数据
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
// 检查用户名和邮箱是否已存在
$stmt = $db->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $email]);
if ($stmt->fetch()) {
$errors[] = "用户名或邮箱已被注册";
}
// 验证密码
if ($password !== $confirmPassword) {
$errors[] = "两次输入密码不一致";
}
// 处理人脸图片上传
if (isset($_FILES['face_image']) && $_FILES['face_image']['error'] === UPLOAD_ERR_OK) {
try {
// 验证图片
if ($_FILES['face_image']['size'] > MAX_IMAGE_SIZE) {
throw new Exception("图片大小不能超过10MB");
}
// 转为Base64
$imageContent = file_get_contents($_FILES['face_image']['tmp_name']);
$base64Image = base64_encode($imageContent);
// 进行人脸检测,确保图片中有人脸
$detectResult = $faceAuth->faceDetect($base64Image);
if (isset($detectResult['error_code']) && $detectResult['error_code'] !== 0) {
throw new Exception("人脸检测失败: " . $detectResult['error_msg']);
}
if ($detectResult['result']['face_num'] === 0) {
throw new Exception("图片中没有检测到人脸");
}
// 检查人脸质量
$faceQuality = $detectResult['result']['face_list'][0]['quality'];
if ($faceQuality['blur'] > 0.8 || $faceQuality['illumination'] < 0.5) {
throw new Exception("人脸图片质量不佳,请更换照片");
}
// 生成唯一用户ID
$userId = md5(uniqid() . time());
// 注册人脸
$registerResult = $faceAuth->faceRegister($base64Image, $userId);
if (isset($registerResult['error_code']) && $registerResult['error_code'] !== 0) {
throw new Exception("人脸注册失败: " . $registerResult['error_msg']);
}
$faceToken = $registerResult['result']['face_token'];
// 保存用户信息
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, email, face_token, user_id) VALUES (?, ?, ?, ?)";
$stmt = $db->prepare($sql);
$stmt->execute([$username, $email, $faceToken, $userId]);
// 记录注册日志
$db->prepare("INSERT INTO auth_logs (user_id, auth_type, result) VALUES (?, 'register', 'success')")
->execute([$userId]);
$_SESSION['success'] = "注册成功!请使用人脸识别登录";
header('Location: login.php');
exit();
} catch (Exception $e) {
$errors[] = $e->getMessage();
}
} else {
$errors[] = "请上传人脸图片";
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">人脸识别注册</title>
<style>
.container { max-width: 600px; margin: 50px auto; padding: 20px; }
.form-group { margin-bottom: 20px; }
.form-group label { display: block; margin-bottom: 5px; }
.form-group input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.error { color: red; margin-bottom: 10px; }
.success { color: green; margin-bottom: 10px; }
#camera-preview { width: 300px; height: 300px; margin: 10px 0; }
</style>
</head>
<body>
<div class="container">
<h2>人脸识别注册</h2>
<?php if (!empty($errors)): ?>
<div class="error">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['success'])): ?>
<div class="success"><?php echo $_SESSION['success']; ?></div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" name="email" required>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>确认密码</label>
<input type="password" name="confirm_password" required>
</div>
<div class="form-group">
<label>人脸图片</label>
<input type="file" name="face_image" accept="image/*" capture="user" required>
</div>
<div class="form-group">
<label>拍照或上传照片</label>
<button type="button" id="camera-button">打开摄像头</button>
<video id="camera-preview" autoplay playsinline style="display:none;"></video>
<canvas id="face-capture" style="display:none;"></canvas>
</div>
<button type="submit">注册</button>
</form>
<p>已有账号?<a href="login.php">登录</a></p>
</div>
<script>
// 摄像头拍照功能
const cameraButton = document.getElementById('camera-button');
const preview = document.getElementById('camera-preview');
const canvas = document.getElementById('face-capture');
const fileInput = document.querySelector('input[name="face_image"]');
cameraButton.addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
preview.srcObject = stream;
preview.style.display = 'block';
// 3秒后自动拍照
setTimeout(() => {
canvas.width = preview.videoWidth;
canvas.height = preview.videoHeight;
canvas.getContext('2d').drawImage(preview, 0, 0);
// 停止摄像头
stream.getTracks().forEach(track => track.stop());
preview.style.display = 'none';
// 将canvas转为文件
canvas.toBlob(blob => {
const file = new File([blob], 'face.png', { type: 'image/png' });
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
}, 'image/png');
}, 3000);
} catch (error) {
console.error('摄像头访问失败:', error);
alert('无法访问摄像头,请使用文件上传');
}
});
</script>
</body>
</html>
登录认证页面
<?php
// login.php
session_start();
require_once 'config/config.php';
require_once 'includes/Database.php';
require_once 'includes/FaceAuth.php';
$db = Database::getInstance()->getConnection();
$faceAuth = new FaceAuth();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['face_data'])) {
// 人脸识别登录
$base64Image = $_POST['face_data'];
// 执行人脸搜索
$searchResult = $faceAuth->faceSearch($base64Image);
// 验证认证结果
$authResult = $faceAuth->validateAuthResult($searchResult);
if ($authResult['success']) {
// 获取用户信息
$stmt = $db->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$authResult['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
// 更新最后登录时间
$db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?")
->execute([$user['id']]);
// 记录认证日志
$db->prepare("INSERT INTO auth_logs (user_id, auth_type, result, score)
VALUES (?, 'login', 'success', ?)")
->execute([$user['id'], $authResult['score']]);
// 设置会话
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['face_auth'] = true;
header('Location: dashboard.php');
exit();
}
} else {
$errors[] = $authResult['error'];
}
} else {
$errors[] = "请进行人脸识别或输入密码";
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">人脸识别登录</title>
<style>
.container { max-width: 600px; margin: 50px auto; padding: 20px; }
#camera { width: 100%; max-width: 400px; height: 300px; margin: 10px 0; }
.btn { padding: 10px 20px; margin: 5px; }
.preview { width: 300px; height: 300px; margin: 10px 0; }
.error { color: red; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="container">
<h2>人脸识别登录</h2>
<?php if (!empty($errors)): ?>
<div class="error">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div id="camera">
<video id="video" width="300" height="300" autoplay playsinline></video>
<canvas id="canvas" width="300" height="300" style="display:none;"></canvas>
</div>
<button id="start-camera" class="btn">开启摄像头</button>
<button id="capture-face" class="btn">拍照识别</button>
<form method="POST" id="login-form">
<input type="hidden" name="face_data" id="face-data">
</form>
<!-- 或者使用文件上传方式 -->
<hr>
<h3>或者上传照片</h3>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="face_image" accept="image/*" capture="user">
<button type="submit" class="btn">上传识别</button>
</form>
<p>还没有账号?<a href="register.php">注册</a></p>
</div>
<script>
let stream = null;
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
document.getElementById('start-camera').addEventListener('click', async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
video.style.display = 'block';
} catch (error) {
console.error('摄像头错误:', error);
alert('无法访问摄像头');
}
});
document.getElementById('capture-face').addEventListener('click', () => {
canvas.getContext('2d').drawImage(video, 0, 0, 300, 300);
const dataUrl = canvas.toDataURL('image/jpeg', 0.9);
// 移除data:image/jpeg;base64,前缀
const base64 = dataUrl.split(',')[1];
document.getElementById('face-data').value = base64;
document.getElementById('login-form').submit();
});
// 文件上传方式
document.querySelector('input[name="face_image"]').addEventListener('change', function() {
if (this.files.length > 0) {
this.form.submit();
}
});
</script>
</body>
</html>
认证回调处理
<?php
// auth_callback.php
require_once 'config/config.php';
require_once 'includes/Database.php';
require_once 'includes/FaceAuth.php';
session_start();
header('Content-Type: application/json');
$db = Database::getInstance()->getConnection();
$faceAuth = new FaceAuth();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => '无效请求']);
exit();
}
try {
// 获取请求类型
$action = $_POST['action'] ?? 'search';
switch ($action) {
case 'search':
// 人脸搜索认证
$imageBase64 = $_POST['image_base64'] ?? '';
if (empty($imageBase64)) {
throw new Exception('缺少图片数据');
}
$result = $faceAuth->faceSearch($imageBase64);
$authResult = $faceAuth->validateAuthResult($result);
if ($authResult['success']) {
// 获取用户信息
$stmt = $db->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$authResult['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
// 更新最后登录时间
$db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?")
->execute([$user['id']]);
// 记录日志
$db->prepare("INSERT INTO auth_logs (user_id, auth_type, result, score, ip_address)
VALUES (?, 'login', 'success', ?, ?)")
->execute([$user['id'], $authResult['score'], $_SERVER['REMOTE_ADDR']]);
// 创建会话
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['face_auth'] = true;
echo json_encode([
'success' => true,
'message' => '人脸识别成功',
'score' => $authResult['score'],
'user' => [
'id' => $user['id'],
'username' => $user['username'],
'email' => $user['email']
]
]);
} else {
throw new Exception('用户不存在');
}
} else {
// 记录失败日志
$db->prepare("INSERT INTO auth_logs (auth_type, result, score, ip_address)
VALUES ('login', 'failed', ?, ?)")
->execute([$authResult['score'] ?? 0, $_SERVER['REMOTE_ADDR']]);
throw new Exception($authResult['error']);
}
break;
case 'detect':
// 人脸检测
$imageBase64 = $_POST['image_base64'] ?? '';
if (empty($imageBase64)) {
throw new Exception('缺少图片数据');
}
$result = $faceAuth->faceDetect($imageBase64);
echo json_encode($result);
break;
case 'compare':
// 人脸比对
$image1 = $_POST['image1'] ?? '';
$image2 = $_POST['image2'] ?? '';
if (empty($image1) || empty($image2)) {
throw new Exception('缺少比对图片数据');
}
$result = $faceAuth->faceCompare($image1, $image2);
echo json_encode($result);
break;
default:
throw new Exception('无效操作类型');
}
} catch (Exception $e) {
echo json_encode([
'success' => false,
'message' => $e->getMessage()
]);
}
?>
用户仪表盘页面
<?php
// dashboard.php
session_start();
// 检查认证状态
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
require_once 'config/config.php';
require_once 'includes/Database.php';
$db = Database::getInstance()->getConnection();
// 获取用户信息
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
session_destroy();
header('Location: login.php');
exit();
}
// 获取认证日志
$stmt = $db->prepare("SELECT * FROM auth_logs WHERE user_id = ? ORDER BY created_at DESC LIMIT 10");
$stmt->execute([$_SESSION['user_id']]);
$logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">用户仪表盘</title>
<style>
.container { max-width: 800px; margin: 50px auto; padding: 20px; }
.user-info { background: #f5f5f5; padding: 20px; border-radius: 5px; }
.logs { margin-top: 30px; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
.logout { float: right; }
</style>
</head>
<body>
<div class="container">
<div class="user-info">
<h2>欢迎, <?php echo htmlspecialchars($user['username']); ?>!</h2>
<p>邮箱: <?php echo htmlspecialchars($user['email']); ?></p>
<p>注册时间: <?php echo $user['created_at']; ?></p>
<p>最后登录: <?php echo $user['last_login']; ?></p>
<form method="POST" action="logout.php">
<button type="submit" class="logout">退出登录</button>
</form>
</div>
<div class="logs">
<h3>最近认证记录</h3>
<table>
<thead>
<tr>
<th>时间</th>
<th>类型</th>
<th>结果</th>
<th>准确率</th>
<th>IP地址</th>
</tr>
</thead>
<tbody>
<?php foreach ($logs as $log): ?>
<tr>
<td><?php echo $log['created_at']; ?></td>
<td><?php echo $log['auth_type']; ?></td>
<td><?php echo $log['result']; ?></td>
<td><?php echo $log['score'] ? number_format($log['score'], 2) . '%' : '-'; ?></td>
<td><?php echo $log['ip_address']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</body>
</html>
认证验证中间件
<?php
// includes/AuthMiddleware.php
class AuthMiddleware {
private $db;
public function __construct() {
$this->db = Database::getInstance()->getConnection();
}
/**
* 会话认证令牌验证
*/
public function validateSession() {
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit();
}
// 验证会话是否有效
$token = $_SESSION['auth_token'] ?? null;
if (!$token) {
$this->logout();
header('Location: login.php');
exit();
}
}
/**
* 生成认证令牌
*/
public function generateAuthToken($userId) {
$token = bin2hex(random_bytes(32));
$sql = "INSERT INTO auth_tokens (user_id, token, expires_at) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 1 HOUR))";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $token]);
return $token;
}
/**
* 验证认证令牌
*/
public function validateAuthToken($token, $userId = null) {
$sql = "SELECT * FROM auth_tokens WHERE token = ? AND expires_at > NOW()";
if ($userId) {
$sql .= " AND user_id = ?";
}
$stmt = $this->db->prepare($sql);
$stmt->execute($userId ? [$token, $userId] : [$token]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ? true : false;
}
/**
* 双因素认证检查
*/
public function checkTwoFactor($userId) {