Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_AntdVue/zh/core/login-log.md
T
2026-09-29 10:14:04 +08:00

8.6 KiB
Raw Blame History

5.15 登录日志

:::tip 概述 登录日志记录用户的登录/登出行为,包括成功和失败记录。每次登录尝试都会记录,便于安全审计和异常行为分析。与操作日志不同,登录日志由 LoginLogic 手动记录,而非中间件自动记录。 :::

记录流程

用户提交登录
    │
    ▼
LoginLogic::login()
    │
    ├─ 创建 RequestInfoService(解析 IP、UA、归属地)
    │
    ├─ 验证验证码 CaptchaService::check()
    │   └─ 失败 → 记录登录日志(status=0, error="验证码错误")
    │
    ├─ 调用 JwtService::login()
    │   ├─ 查询用户 → 不存在 → 记录登录日志(status=0, error="用户名或密码错误")
    │   ├─ 校验状态 → 禁用 → 记录登录日志(status=0, error="账号已被禁用")
    │   ├─ 校验密码 → 错误 → 记录登录日志(status=0, error="用户名或密码错误")
    │   └─ 签发 Token → 成功
    │
    ├─ 记录登录日志(status=1, 无错误)
    │
    └─ 返回 { access_token, refresh_token, user }

登录/登出记录

┌─────────────────────────────────────────────────────────────┐
│ 登录成功                                                      │
│ LoginLogic::login() → 成功 → recordLoginLog(username, 1, 0)  │
│ LoginController::login() → 成功日志                           │
├─────────────────────────────────────────────────────────────┤
│ 登录失败                                                      │
│ LoginLogic::login() → 异常 → recordLoginLog(username, 0, 1)  │
│ LoginController::login() → 失败日志                           │
├─────────────────────────────────────────────────────────────┤
│ 登出                                                          │
│ LoginController::logout() → recordLoginLog(username, 2, 0)   │
└─────────────────────────────────────────────────────────────┘

记录字段

字段 类型 说明 示例
username VARCHAR(50) 用户名(即使登录失败也记录) admin
type TINYINT 类型:1=登录 2=登出 1
status TINYINT 状态:0=失败 1=成功 1
message VARCHAR(200) 结果消息 登录成功 / 密码错误
ip VARCHAR(50) 客户端 IP 192.168.1.100
location VARCHAR(100) IP 归属地 江苏省南京市
os VARCHAR(100) 操作系统 Windows 10
browser VARCHAR(100) 浏览器 Chrome 120
create_time DATETIME 登录时间 2026-09-22 10:30:00

核心代码

登录记录

// app/logic/LoginLogic.php

public function login(string $username, string $password, string $captchaKey, string $captchaCode): array
{
    $requestInfo = RequestInfoService::create(request());

    try {
        // 验证验证码
        if (!CaptchaService::check($captchaKey, $captchaCode)) {
            throw new \Exception('验证码错误');
        }

        // 调用 JwtService 登录
        $result = $this->jwtService->login($username, $password);

        // 记录成功日志
        $this->recordLoginLog($username, 1, 0, '', '登录成功');

        return $result;

    } catch (\Exception $e) {
        // 记录失败日志
        $this->recordLoginLog($username, 1, 1, $e->getMessage(), '登录失败');

        throw $e;
    }
}

统一记录方法

/**
 * 记录登录日志
 *
 * @param string $username 用户名
 * @param int $type 类型:1=登录 2=登出
 * @param int $status 状态:0=成功 1=失败
 * @param string $error 错误信息(成功时为空)
 * @param string $message 结果消息
 */
public function recordLoginLog(string $username, int $type, int $status, string $error = '', string $message = ''): void
{
    $requestInfo = RequestInfoService::create(request());

    try {
        LoginLog::create([
            'username'  => $username,
            'type'      => $type,
            'status'    => $status,
            'message'   => $message,
            'ip'        => $requestInfo->getIp(),
            'location'  => $requestInfo->getIpLocation(),
            'os'        => $requestInfo->getOs(),
            'browser'   => $requestInfo->getBrowser(),
        ]);
    } catch (\Exception $e) {
        // 日志记录失败不影响业务
    }
}

登出记录

// app/controller/LoginController.php

#[Log('系统登录-用户登出', Log::TYPE_LOGOUT)]
public function logout(): Json
{
    $token = \jwt\Jwt::getTokenFromHeader();
    if ($token) {
        $this->logic->recordLoginLog($this->getLoginUsername(), 2, 0, '', '退出成功');
    }
    return $this->success(null, '登出成功');
}

API 接口

方法 路径 权限码 说明
GET /api/login/log/page sys:login:log:list 分页列表
GET /api/login/log/detail/:id sys:login:log:detail 详情
DELETE /api/login/log/delete/:id sys:login:log:delete 删除
DELETE /api/login/log/batchDelete sys:login:log:delete 批量删除

安全分析

暴力破解检测

-- 短时间内同一 IP 大量失败登录(5分钟内失败超过10次)
SELECT ip, COUNT(*) as fail_count, MIN(create_time) as first_time, MAX(create_time) as last_time
FROM think_login_log
WHERE status = 0
  AND create_time > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
GROUP BY ip
HAVING fail_count >= 10
ORDER BY fail_count DESC;

异常账号检测

-- 同一账号短时间内大量失败(可能被暴力破解)
SELECT username, COUNT(*) as fail_count
FROM think_login_log
WHERE status = 0
  AND create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY username
HAVING fail_count >= 5
ORDER BY fail_count DESC;

异地登录检测

-- 同一用户在不同城市短时间内登录
SELECT username, location, COUNT(*) as login_count
FROM think_login_log
WHERE status = 1
  AND create_time > DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY username, location
HAVING login_count >= 1;

高频恶意 IP

-- 统计失败登录最多的 IP(最近7天)
SELECT ip, location, COUNT(*) as fail_count
FROM think_login_log
WHERE status = 0
  AND create_time > DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY ip, location
ORDER BY fail_count DESC
LIMIT 20;

成功率分析

-- 最近30天登录成功率
SELECT
    DATE(create_time) as date,
    COUNT(*) as total,
    SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as success,
    ROUND(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) as success_rate
FROM think_login_log
WHERE type = 1
  AND create_time > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(create_time)
ORDER BY date;

与操作日志的区别

维度 登录日志 操作日志
表 think_login_log think_operation_log
记录方 LoginLogic 手动记录 LogMiddleware 自动记录
记录内容 仅登录/登出 所有写操作 + 导出/导入
触发方式 代码中调用 recordLoginLog() #[Log] 注解
用途 登录安全审计 业务操作审计
失败也记录 ✓(登录失败必须记录) ✗(仅记录成功的写操作)

日志保留策略

策略 说明 建议保留时间
按时间清理 定期删除 N 天前的日志 180 天(6个月)
安全合规 某些行业要求保留更长时间 按合规要求
-- 示例:清理 180 天前的登录日志
DELETE FROM think_login_log WHERE create_time < DATE_SUB(NOW(), INTERVAL 180 DAY);

最佳实践

实践 说明
成功和失败都记录 安全审计需要完整的登录行为记录
统一错误提示 "用户名或密码错误"不区分用户不存在和密码错误
定期分析 定期检查异常登录模式(暴力破解、异地登录)
IP 黑名单 将高频恶意 IP 加入黑名单(Nginx/防火墙层)
配合验证码 验证码防刷 + 登录日志分析形成双重防护
日志不可篡改 登录日志仅 INSERT,无 UPDATE 接口