本文目录导读:

我来为您设计一个PHP项目异常登录检测提醒系统,这个系统可以检测和提醒用户异常登录行为,提高账户安全性。
数据库设计
-- 用户登录日志表
CREATE TABLE user_login_logs (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
login_time DATETIME NOT NULL,
ip_address VARCHAR(45) NOT NULL,
user_agent TEXT,
login_status ENUM('success', 'failed', 'suspicious') DEFAULT 'success',
location VARCHAR(255),
risk_score DECIMAL(3,2) DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_login_time (login_time),
INDEX idx_ip_address (ip_address)
);
-- 用户安全设置表
CREATE TABLE user_security_settings (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL UNIQUE,
trusted_devices JSON,
trusted_locations JSON,
login_notification BOOLEAN DEFAULT TRUE,
suspicious_login_alert BOOLEAN DEFAULT TRUE,
max_failed_attempts INT DEFAULT 5,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 异常登录警报表
CREATE TABLE login_alerts (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
alert_type VARCHAR(50) NOT NULL,
alert_message TEXT,
ip_address VARCHAR(45),
detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_read BOOLEAN DEFAULT FALSE,
action_taken VARCHAR(255),
INDEX idx_user_id (user_id),
INDEX idx_detected_at (detected_at)
);
异常检测核心类
<?php
// AnomalyDetection.php
class AnomalyDetection {
private $db;
private $config;
public function __construct($db, $config = []) {
$this->db = $db;
$this->config = array_merge([
'time_window' => 3600, // 1小时的检测窗口
'max_login_attempts' => 10,
'geo_change_alert' => true,
'device_change_alert' => true,
'time_pattern_alert' => true
], $config);
}
/**
* 检测登录异常
*/
public function detectAnomaly($userId, $currentIp, $userAgent, $loginTime = null) {
$loginTime = $loginTime ?? date('Y-m-d H:i:s');
$anomalies = [];
// 1. 检测地理位置变化
if ($this->config['geo_change_alert']) {
$geoAnomaly = $this->detectGeoChange($userId, $currentIp);
if ($geoAnomaly) {
$anomalies[] = $geoAnomaly;
}
}
// 2. 检测设备变化
if ($this->config['device_change_alert']) {
$deviceAnomaly = $this->detectDeviceChange($userId, $userAgent);
if ($deviceAnomaly) {
$anomalies[] = $deviceAnomaly;
}
}
// 3. 检测登录频率异常
$frequencyAnomaly = $this->detectFrequencyAnomaly($userId, $loginTime);
if ($frequencyAnomaly) {
$anomalies[] = $frequencyAnomaly;
}
// 4. 检测异常时间段
if ($this->config['time_pattern_alert']) {
$timeAnomaly = $this->detectTimePatternAnomaly($userId, $loginTime);
if ($timeAnomaly) {
$anomalies[] = $timeAnomaly;
}
}
// 计算风险评分
$riskScore = $this->calculateRiskScore($anomalies);
return [
'is_anomaly' => !empty($anomalies),
'anomalies' => $anomalies,
'risk_score' => $riskScore,
'total_checks' => 4,
'failed_checks' => count($anomalies)
];
}
/**
* 检测地理位置变化
*/
private function detectGeoChange($userId, $currentIp) {
$lastLogin = $this->getLastLogin($userId);
if (!$lastLogin) {
return null;
}
$currentLocation = $this->getLocationFromIp($currentIp);
$lastLocation = $this->getLocationFromIp($lastLogin['ip_address']);
if ($currentLocation && $lastLocation) {
$distance = $this->calculateDistance(
$currentLocation['lat'], $currentLocation['lon'],
$lastLocation['lat'], $lastLocation['lon']
);
// 如果距离超过1000公里且在短时间内,标记为异常
if ($distance > 1000) {
$timeDiff = strtotime(date('Y-m-d H:i:s')) - strtotime($lastLogin['login_time']);
if ($timeDiff < 7200) { // 2小时内跨越1000公里
return [
'type' => 'geo_change',
'severity' => 'high',
'message' => "异常地理位置变化: 从 {$lastLocation['city']} 到 {$currentLocation['city']}",
'distance' => $distance . 'km',
'time_diff' => $timeDiff . '秒'
];
}
}
}
return null;
}
/**
* 检测设备变化
*/
private function detectDeviceChange($userId, $currentUserAgent) {
$lastLogins = $this->getLastLogins($userId, 5);
if (empty($lastLogins)) {
return null;
}
$deviceFingerprint = $this->generateDeviceFingerprint($currentUserAgent);
$knownDevices = [];
foreach ($lastLogins as $login) {
$fingerprint = $this->generateDeviceFingerprint($login['user_agent']);
$knownDevices[$fingerprint] = true;
}
if (!isset($knownDevices[$deviceFingerprint])) {
return [
'type' => 'device_change',
'severity' => 'medium',
'message' => '从未知设备登录',
'device_fingerprint' => $deviceFingerprint
];
}
return null;
}
/**
* 检测登录频率异常
*/
private function detectFrequencyAnomaly($userId, $loginTime) {
$windowStart = date('Y-m-d H:i:s', strtotime($loginTime) - $this->config['time_window']);
$stmt = $this->db->prepare("
SELECT COUNT(*) as count
FROM user_login_logs
WHERE user_id = ?
AND login_time >= ?
AND login_time <= ?
");
$stmt->execute([$userId, $windowStart, $loginTime]);
$result = $stmt->fetch();
if ($result['count'] > $this->config['max_login_attempts']) {
return [
'type' => 'frequency',
'severity' => 'high',
'message' => "短时间内多次登录: {$result['count']} 次在 " . ($this->config['time_window'] / 3600) . " 小时内",
'count' => $result['count'],
'time_window' => $this->config['time_window']
];
}
return null;
}
/**
* 检测异常时间段
*/
private function detectTimePatternAnomaly($userId, $loginTime) {
$hour = date('H', strtotime($loginTime));
$dayOfWeek = date('w', strtotime($loginTime));
// 获取用户通常登录时间段
$stmt = $this->db->prepare("
SELECT HOUR(login_time) as hour,
DAYOFWEEK(login_time) as day_of_week,
COUNT(*) as count
FROM user_login_logs
WHERE user_id = ?
AND login_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY HOUR(login_time), DAYOFWEEK(login_time)
ORDER BY count DESC
LIMIT 10
");
$stmt->execute([$userId]);
$patterns = $stmt->fetchAll();
if (!empty($patterns)) {
$usualHours = array_column($patterns, 'hour');
$usualDays = array_column($patterns, 'day_of_week');
if (!in_array($hour, $usualHours) && !in_array($dayOfWeek, $usualDays)) {
return [
'type' => 'time_pattern',
'severity' => 'low',
'message' => "在异常时间段登录: {$hour}:00, 星期" . $dayOfWeek,
'current_hour' => $hour,
'current_day' => $dayOfWeek
];
}
}
return null;
}
/**
* 计算风险评分
*/
private function calculateRiskScore($anomalies) {
$score = 0;
foreach ($anomalies as $anomaly) {
switch ($anomaly['severity']) {
case 'high':
$score += 0.4;
break;
case 'medium':
$score += 0.25;
break;
case 'low':
$score += 0.1;
break;
}
}
return min($score, 1.0);
}
/**
* 获取用户最后登录记录
*/
private function getLastLogin($userId) {
$stmt = $this->db->prepare("
SELECT * FROM user_login_logs
WHERE user_id = ?
AND login_status = 'success'
ORDER BY login_time DESC
LIMIT 1
");
$stmt->execute([$userId]);
return $stmt->fetch();
}
/**
* 获取用户最近登录记录
*/
private function getLastLogins($userId, $limit = 5) {
$stmt = $this->db->prepare("
SELECT * FROM user_login_logs
WHERE user_id = ?
ORDER BY login_time DESC
LIMIT ?
");
$stmt->execute([$userId, $limit]);
return $stmt->fetchAll();
}
/**
* 从IP获取地理位置
*/
private function getLocationFromIp($ip) {
// 可以使用第三方API,如ip-api.com
$response = @file_get_contents("http://ip-api.com/json/{$ip}");
if ($response) {
$data = json_decode($response, true);
if ($data['status'] === 'success') {
return [
'lat' => $data['lat'],
'lon' => $data['lon'],
'city' => $data['city'],
'country' => $data['country']
];
}
}
return null;
}
/**
* 计算两点间的距离(Haversine公式)
*/
private function calculateDistance($lat1, $lon1, $lat2, $lon2) {
$radius = 6371; // 地球半径(km)
$dlat = deg2rad($lat2 - $lat1);
$dlon = deg2rad($lon2 - $lon1);
$a = sin($dlat/2) * sin($dlat/2) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
sin($dlon/2) * sin($dlon/2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
return $radius * $c;
}
/**
* 生成设备指纹
*/
private function generateDeviceFingerprint($userAgent) {
return md5($userAgent);
}
}
警报通知系统
<?php
// AlertNotification.php
class AlertNotification {
private $db;
private $emailSender;
public function __construct($db, $emailSender) {
$this->db = $db;
$this->emailSender = $emailSender;
}
/**
* 发送异常登录警报
*/
public function sendAlert($userId, $anomalyData) {
// 获取用户信息
$user = $this->getUserInfo($userId);
$securitySettings = $this->getSecuritySettings($userId);
if (!$securitySettings['suspicious_login_alert']) {
return false;
}
// 记录警报
$alertId = $this->logAlert($userId, $anomalyData);
// 发送通知
$notificationsSent = [];
// 1. 邮件通知
$emailSent = $this->sendEmailAlert($user, $anomalyData);
if ($emailSent) {
$notificationsSent[] = 'email';
}
// 2. 短信通知(如果配置了)
if (!empty($user['phone'])) {
$smsSent = $this->sendSmsAlert($user['phone'], $anomalyData);
if ($smsSent) {
$notificationsSent[] = 'sms';
}
}
// 3. 推送通知(如果系统支持)
if (!empty($user['push_token'])) {
$pushSent = $this->sendPushNotification($user['push_token'], $anomalyData);
if ($pushSent) {
$notificationsSent[] = 'push';
}
}
return [
'alert_id' => $alertId,
'notifications_sent' => $notificationsSent
];
}
/**
* 发送邮件警报
*/
private function sendEmailAlert($user, $anomalyData) {
$subject = "⚠️ 安全警报: 检测到异常登录活动";
$message = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.alert-box {
background-color: #fff3cd;
border: 1px solid #ffc107;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
}
.high-risk {
background-color: #f8d7da;
border-color: #f5c6cb;
}
.info {
margin: 10px 0;
padding: 10px;
background-color: #f8f9fa;
border-radius: 5px;
}
</style>
</head>
<body>
<h2>异常登录检测警报</h2>
<div class='alert-box'>
<h3>检测到以下异常:</h3>
<ul>
";
foreach ($anomalyData['anomalies'] as $anomaly) {
$message .= "<li><strong>{$anomaly['type']}:</strong> {$anomaly['message']}</li>";
}
$message .= "
</ul>
</div>
<div class='info'>
<p><strong>风险评分:</strong> " . ($anomalyData['risk_score'] * 100) . "%</p>
<p><strong>登录时间:</strong> " . date('Y-m-d H:i:s') . "</p>
<p><strong>IP地址:</strong> {$this->getClientIp()}</p>
</div>
<div class='actions'>
<h3>建议操作:</h3>
<ul>
<li>如果是您本人操作,请忽略此邮件</li>
<li>如果不是您操作的,请立即修改密码</li>
<li>检查账户安全设置,启用双因素认证</li>
<li>查看最近的登录活动</li>
</ul>
<p>
<a href='https://yourdomain.com/security/check'
style='background-color: #007bff; color: white; padding: 10px 20px;
text-decoration: none; border-radius: 5px;'>
查看安全设置
</a>
</p>
</div>
</body>
</html>
";
return $this->emailSender->sendHtml(
$user['email'],
$subject,
$message
);
}
/**
* 发送短信警报
*/
private function sendSmsAlert($phone, $anomalyData) {
$riskLevel = $anomalyData['risk_score'] > 0.5 ? '高风险' : '中等风险';
$anomalyTypes = array_map(function($a) { return $a['type']; }, $anomalyData['anomalies']);
$message = "[安全警报] 您的账户检测到{$riskLevel}登录异常: "
. implode(', ', $anomalyTypes)
. ",IP: {$this->getClientIp()} 时间: "
. date('H:i:s');
// 使用短信服务发送
return $this->sendSms($phone, $message);
}
/**
* 记录警报到数据库
*/
private function logAlert($userId, $anomalyData) {
$stmt = $this->db->prepare("
INSERT INTO login_alerts
(user_id, alert_type, alert_message, ip_address)
VALUES (?, ?, ?, ?)
");
$stmt->execute([
$userId,
'suspicious_login',
json_encode($anomalyData),
$this->getClientIp()
]);
return $this->db->lastInsertId();
}
/**
* 获取用户信息
*/
private function getUserInfo($userId) {
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
return $stmt->fetch();
}
/**
* 获取用户安全设置
*/
private function getSecuritySettings($userId) {
$stmt = $this->db->prepare("
SELECT * FROM user_security_settings WHERE user_id = ?
");
$stmt->execute([$userId]);
return $stmt->fetch() ?: ['suspicious_login_alert' => true];
}
/**
* 获取客户端IP
*/
private function getClientIp() {
$ipAddress = '';
if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipAddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipAddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipAddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipAddress = $_SERVER['HTTP_FORWARDED'];
else if(isset($_SERVER['REMOTE_ADDR']))
$ipAddress = $_SERVER['REMOTE_ADDR'];
else
$ipAddress = 'UNKNOWN';
return $ipAddress;
}
}
登录处理集成
<?php
// LoginProcessor.php
class LoginProcessor {
private $db;
private $anomalyDetection;
private $alertNotification;
public function __construct($db) {
$this->db = $db;
$this->anomalyDetection = new AnomalyDetection($db);
$this->alertNotification = new AlertNotification($db, new EmailSender());
}
/**
* 处理登录
*/
public function processLogin($username, $password) {
// 验证用户凭据
$user = $this->authenticateUser($username, $password);
if (!$user) {
$this->logFailedLogin($username);
return ['success' => false, 'message' => '用户名或密码错误'];
}
// 检测登录异常
$clientIp = $this->getClientIp();
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$anomalyResult = $this->anomalyDetection->detectAnomaly(
$user['id'],
$clientIp,
$userAgent
);
// 记录登录日志
$this->logLogin($user['id'], $clientIp, $userAgent,
$anomalyResult['is_anomaly'] ? 'suspicious' : 'success',
$anomalyResult['risk_score']);
// 如果检测到异常,发送警报
if ($anomalyResult['is_anomaly']) {
$alertResult = $this->alertNotification->sendAlert(
$user['id'],
$anomalyResult
);
// 根据风险等级决定是否允许登录
if ($anomalyResult['risk_score'] > 0.7) {
// 高风险:要求额外验证
return [
'success' => false,
'require_verification' => true,
'message' => '检测到高风险登录,需要进行额外验证',
'anomaly' => $anomalyResult,
'alert' => $alertResult
];
}
}
// 安全登录
$_SESSION['user_id'] = $user['id'];
$_SESSION['login_time'] = time();
$_SESSION['login_token'] = bin2hex(random_bytes(32));
return [
'success' => true,
'user' => $user,
'anomaly' => $anomalyResult
];
}
/**
* 记录登录日志
*/
private function logLogin($userId, $ip, $userAgent, $status, $riskScore = 0) {
$stmt = $this->db->prepare("
INSERT INTO user_login_logs
(user_id, login_time, ip_address, user_agent, login_status, risk_score)
VALUES (?, NOW(), ?, ?, ?, ?)
");
$stmt->execute([
$userId,
$ip,
$userAgent,
$status,
$riskScore
]);
}
/**
* 记录失败登录
*/
private function logFailedLogin($username) {
$stmt = $this->db->prepare("
INSERT INTO user_login_logs
(user_id, login_time, ip_address, user_agent, login_status)
VALUES (?, NOW(), ?, ?, 'failed')
");
$stmt->execute([
null,
$this->getClientIp(),
$_SERVER['HTTP_USER_AGENT'] ?? ''
]);
}
/**
* 验证用户凭据
*/
private function authenticateUser($username, $password) {
$stmt = $this->db->prepare("
SELECT * FROM users
WHERE (username = ? OR email = ?)
AND status = 'active'
LIMIT 1
");
$stmt->execute([$username, $username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
return $user;
}
return false;
}
private function getClientIp() {
// 同上AlertNotification中的getClientIp方法
// ...
}
}
用户安全面板
<?php
// SecurityDashboard.php
class SecurityDashboard {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 获取用户安全概览
*/
public function getSecurityOverview($userId) {
return [
'recent_logins' => $this->getRecentLogins($userId, 10),
'active_alerts' => $this->getActiveAlerts($userId),
'security_settings' => $this->getSecuritySettings($userId),
'risk_trend' => $this->getRiskTrend($userId, 30)
];
}
/**
* 获取最近登录记录
*/
public function getRecentLogins($userId, $limit = 10) {
$stmt = $this->db->prepare("
SELECT * FROM user_login_logs
WHERE user_id = ?
ORDER BY login_time DESC
LIMIT ?
");
$stmt->execute([$userId, $limit]);
return $stmt->fetchAll();
}
/**
* 获取活跃警报
*/
public function getActiveAlerts($userId) {
$stmt = $this->db->prepare("
SELECT * FROM login_alerts
WHERE user_id = ?
AND is_read = FALSE
ORDER BY detected_at DESC
");
$stmt->execute([$userId]);
return $stmt->fetchAll();
}
/**
* 标记警报为已读
*/
public function markAlertAsRead($alertId, $userId) {
$stmt = $this->db->prepare("
UPDATE login_alerts
SET is_read = TRUE
WHERE id = ? AND user_id = ?
");
return $stmt->execute([$alertId, $userId]);
}
/**
* 获取风险趋势数据
*/
public function getRiskTrend($userId, $days = 30) {
$stmt = $this->db->prepare("
SELECT DATE(login_time) as date,
AVG(risk_score) as avg_risk,
COUNT(*) as login_count,
SUM(CASE WHEN login_status = 'suspicious' THEN 1 ELSE 0 END) as suspicious_count
FROM user_login_logs
WHERE user_id = ?
AND login_time >= DATE_SUB(NOW(), INTERVAL ? DAY)
GROUP BY DATE(login_time)
ORDER BY date DESC
");
$stmt->execute([$userId, $days]);
return $stmt->fetchAll();
}
}
使用示例
<?php
// example_usage.php
// 初始化数据库连接
$db = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password');
// 登录处理
$loginProcessor = new LoginProcessor($db);
$result = $loginProcessor->processLogin($_POST['username'], $_POST['password']);
if ($result['success']) {
// 登录成功
if (!empty($result['anomaly']['anomalies'])) {
// 有异常但风险较低
$_SESSION['warning'] = '检测到一些异常登录特征,请留意';
}
header('Location: /dashboard');
} else {
if (isset($result['require_verification'])) {
// 需要额外验证
$_SESSION['require_verification'] = true;
$_SESSION['anomaly_data'] = $result['anomaly'];
header('Location: /verify-login');
} else {
// 登录失败
$error = $result['message'];
}
}
// 用户查看安全面板
$securityDashboard = new SecurityDashboard($db);
$securityData = $securityDashboard->getSecurityOverview($_SESSION['user_id']);
// 输出安全面板
echo json_encode($securityData);
前端提醒脚本
<!-- security_alert.js -->
<script>
class SecurityAlertManager {
constructor(options = {}) {
this.options = Object.assign({
checkInterval: 60000, // 每分钟检查一次
apiEndpoint: '/api/security/check-alerts',
soundEnabled: true,
notificationEnabled: true
}, options);
this.init();
}
init() {
// 创建提醒容器
this.createAlertContainer();
// 开始定期检查
this.startPeriodicCheck();
// 监听用户活动
this.monitorUserActivity();
}
createAlertContainer() {
const container = document.createElement('div');
container.id = 'security-alert-container';
container.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
max-width: 400px;
`;
document.body.appendChild(container);
}
startPeriodicCheck() {
this.checkForAlerts();
setInterval(() => this.checkForAlerts(), this.options.checkInterval);
}
async checkForAlerts() {
try {
const response = await fetch(this.options.apiEndpoint, {
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content
}
});
const data = await response.json();
if (data.alerts && data.alerts.length > 0) {
this.showAlerts(data.alerts);
this.playAlertSound();
if (this.options.notificationEnabled) {
this.showBrowserNotification(data.alerts);
}
}
} catch (error) {
console.error('安全检查失败:', error);
}
}
showAlerts(alerts) {
const container = document.getElementById('security-alert-container');
alerts.forEach(alert => {
const alertElement = this.createAlertElement(alert);
container.appendChild(alertElement);
// 10秒后自动消失
setTimeout(() => {
alertElement.classList.add('fade-out');
setTimeout(() => alertElement.remove(), 500);
}, 10000);
});
}
createAlertElement(alert) {
const severityColors = {
'high': '#dc3545',
'medium': '#ffc107',
'low': '#17a2b8'
};
const color = severityColors[alert.severity] || '#6c757d';
const element = document.createElement('div');
element.style.cssText = `
background: white;
border-left: 4px solid ${color};
border-radius: 4px;
padding: 15px;
margin-bottom: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
animation: slideIn 0.3s ease-out;
`;
element.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: start;">
<h4 style="margin: 0; color: ${color};">
⚠️ ${this.getAlertTypeText(alert.type)}
</h4>
<button onclick="this.parentElement.parentElement.remove()"
style="background: none; border: none; cursor: pointer;">
×
</button>
</div>
<p style="margin: 10px 0 0; color: #666;">
${alert.message}
</p>
<small style="color: #999;">
${new Date(alert.detected_at).toLocaleString()}
</small>
<div style="margin-top: 10px;">
<a href="/security/check" style="color: #007bff; text-decoration: none;">
查看详情 →
</a>
</div>
`;
return element;
}
getAlertTypeText(type) {
const types = {
'geo_change': '地理位置异常',
'device_change': '新设备登录',
'frequency': '登录频率异常',
'time_pattern': '异常登录时间'
};
return types[type] || '安全警报';
}
playAlertSound() {
if (!this.options.soundEnabled) return;
const audio = new Audio();
audio.src = '/sounds/alert.mp3';
audio.volume = 0.3;
audio.play().catch(() => {}); // 忽略自动播放错误
}
showBrowserNotification(alerts) {
if (!('Notification' in window)) return;
if (Notification.permission === 'granted') {
const alertCount = alerts.length;
const body = alertCount > 1
? `发现 ${alertCount} 个安全警报`
: `安全警报: ${alerts[0].message}`;
new Notification('安全警报', {
body: body,
icon: '/images/security-icon.png'
});
} else if (Notification.permission !== 'denied') {
Notification.requestPermission();
}
}
monitorUserActivity() {
// 监听用户离开页面事件
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
this.checkForAlerts();
}
});
}
}
// 初始化安全提醒管理器
document.addEventListener('DOMContentLoaded', () => {
new SecurityAlertManager({
checkInterval: 60000,
soundEnabled: true,
notificationEnabled: true
});
});
</script>
API端点
<?php
// api/security.php
header('Content-Type: application/json');
// CSRF保护
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
http_response_code(403);
die(json_encode(['error' => 'CSRF token mismatch']));
}
}
$action = $_GET['action'] ?? '';
switch ($action) {
case 'check-alerts':
// 检查未读警报
$securityDashboard = new SecurityDashboard($db);
$alerts = $securityDashboard->getActiveAlerts($_SESSION['user_id']);
echo json_encode(['alerts' => $alerts]);
break;
case 'mark-alert-read':
// 标记警报为已读
$alertId = $_POST['alert_id'];
$securityDashboard = new SecurityDashboard($db);
$result = $securityDashboard->markAlertAsRead($alertId, $_SESSION['user_id']);
echo json_encode(['success' => $result]);
break;
case 'login-history':
// 获取登录历史
$securityDashboard = new SecurityDashboard($db);
$history = $securityDashboard->getRecentLogins($_SESSION['user_id'], $_GET['limit'] ?? 10);
echo json_encode(['history' => $history]);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Invalid action']);
}
这个异常登录检测提醒系统提供了完整的解决方案,包括:
- 多维度异常检测:地理位置、设备、频率、时间段
- 风险评分系统:自动计算登录风险等级
- 多通道通知:邮件、短信、推送通知
- 用户安全面板:查看登录历史和警报记录
- 前端实时提醒:浏览器通知和声音提示
- 安全措施建议:密码修改、双因素认证等
您可以根据实际需求调整检测阈值、通知方式和安全策略。