- 添加认证相关API文档(登录认证、验证码) - 添加权限管理API文档(菜单管理、角色管理) - 添加系统管理API文档(图片管理、文件管理、登录日志) - 添加用户管理API文档 - 完善项目API文档结构,提升开发体验
27 KiB
登录日志API
概述
登录日志模块用于记录和管理系统用户的登录行为,包括登录成功、登录失败、退出登录等操作的详细记录。提供日志查询、统计分析和安全监控功能,是系统安全审计的重要组成部分。
权限说明
登录日志接口需要相应的权限才能访问:
| 操作 | 权限码 | 说明 |
|---|---|---|
| 查询登录日志 | system:loginlog:list |
查看登录日志权限 |
| 删除登录日志 | system:loginlog:remove |
删除登录日志权限 |
| 导出登录日志 | system:loginlog:export |
导出登录日志权限 |
接口列表
1. 分页查询登录日志
接口地址: GET /coder/sysLoginLog/listPage
接口描述: 分页查询系统登录日志列表
是否需要认证: 是
权限要求: system:loginlog:list
请求头:
Authorization: Bearer your-token-value
请求参数:
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| pageNo | Integer | 否 | 页码 | 1 |
| pageSize | Integer | 否 | 每页大小 | 10 |
| loginName | String | 否 | 登录账号 | admin |
| loginStatus | String | 否 | 登录状态 | 0 |
| clientType | String | 否 | 客户端类型 | WEB |
| loginIp | String | 否 | 登录IP | 127.0.0.1 |
| loginAddress | String | 否 | 登录地址 | 本地登录 |
| browser | String | 否 | 浏览器类型 | Chrome |
| os | String | 否 | 操作系统 | Windows 10 |
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
| endTime | String | 否 | 结束时间 | 2024-12-31 |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": {
"records": [
{
"infoId": 1,
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"deviceName": "Windows PC",
"loginIp": "127.0.0.1",
"loginAddress": "本地登录",
"browser": "Chrome 120.0.0.0",
"os": "Windows 10",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"loginTime": "2024-07-05 10:00:00",
"logoutTime": "2024-07-05 11:00:00",
"sessionDuration": 3600,
"failureReason": "",
"remark": "登录成功",
"createBy": "system",
"createTime": "2024-07-05 10:00:00",
"updateBy": "system",
"updateTime": "2024-07-05 11:00:00"
}
],
"total": 1,
"size": 10,
"current": 1,
"pages": 1
},
"traceId": "trace-123456"
}
调用示例:
curl -X GET \
"http://localhost:18099/coder/sysLoginLog/listPage?pageNo=1&pageSize=10&loginName=admin&loginStatus=0" \
-H "Authorization: Bearer your-token-value"
2. 查询所有登录日志
接口地址: GET /coder/sysLoginLog/list
接口描述: 查询所有系统登录日志(不分页)
是否需要认证: 是
权限要求: system:loginlog:list
请求参数: 同分页查询(除pageNo、pageSize外)
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": [
{
"infoId": 1,
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"deviceName": "Windows PC",
"loginIp": "127.0.0.1",
"loginAddress": "本地登录",
"browser": "Chrome 120.0.0.0",
"os": "Windows 10",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"loginTime": "2024-07-05 10:00:00",
"logoutTime": "2024-07-05 11:00:00",
"sessionDuration": 3600,
"failureReason": "",
"remark": "登录成功",
"createBy": "system",
"createTime": "2024-07-05 10:00:00",
"updateBy": "system",
"updateTime": "2024-07-05 11:00:00"
}
],
"traceId": "trace-123456"
}
调用示例:
curl -X GET \
"http://localhost:18099/coder/sysLoginLog/list?loginStatus=1&beginTime=2024-07-01" \
-H "Authorization: Bearer your-token-value"
3. 根据ID查询登录日志
接口地址: GET /coder/sysLoginLog/getById/{id}
接口描述: 根据日志ID查询登录日志详细信息
是否需要认证: 是
权限要求: system:loginlog:list
路径参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 是 | 日志ID |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": {
"infoId": 1,
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"deviceName": "Windows PC",
"loginIp": "127.0.0.1",
"loginAddress": "本地登录",
"browser": "Chrome 120.0.0.0",
"os": "Windows 10",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"loginTime": "2024-07-05 10:00:00",
"logoutTime": "2024-07-05 11:00:00",
"sessionDuration": 3600,
"failureReason": "",
"remark": "登录成功",
"createBy": "system",
"createTime": "2024-07-05 10:00:00",
"updateBy": "system",
"updateTime": "2024-07-05 11:00:00"
},
"traceId": "trace-123456"
}
调用示例:
curl -X GET \
http://localhost:18099/coder/sysLoginLog/getById/1 \
-H "Authorization: Bearer your-token-value"
4. 新增登录日志
接口地址: POST /coder/sysLoginLog/add
接口描述: 新增系统登录日志记录
是否需要认证: 是
权限要求: system:loginlog:add
请求参数:
{
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"deviceName": "Windows PC",
"loginIp": "127.0.0.1",
"loginAddress": "本地登录",
"browser": "Chrome 120.0.0.0",
"os": "Windows 10",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"loginTime": "2024-07-05 10:00:00",
"failureReason": "",
"remark": "登录成功"
}
请求参数说明:
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
| loginName | String | 是 | 登录账号 | 不能为空 |
| userName | String | 否 | 用户姓名 | 可为空 |
| userId | Long | 否 | 用户ID | 有效的用户ID |
| loginStatus | String | 是 | 登录状态 | 0-成功 1-失败 |
| clientType | String | 是 | 客户端类型 | WEB, MOBILE, API |
| deviceName | String | 否 | 设备名称 | 可为空 |
| loginIp | String | 是 | 登录IP | 有效的IP地址 |
| loginAddress | String | 否 | 登录地址 | 可为空 |
| browser | String | 否 | 浏览器信息 | 可为空 |
| os | String | 否 | 操作系统 | 可为空 |
| userAgent | String | 否 | 用户代理 | 可为空 |
| loginTime | String | 是 | 登录时间 | 时间格式 |
| failureReason | String | 否 | 失败原因 | 登录失败时必填 |
| remark | String | 否 | 备注信息 | 最长500字符 |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": "新增成功",
"traceId": "trace-123456"
}
调用示例:
curl -X POST \
http://localhost:18099/coder/sysLoginLog/add \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token-value" \
-d '{
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"loginIp": "127.0.0.1",
"loginTime": "2024-07-05 10:00:00",
"remark": "登录成功"
}'
5. 修改登录日志
接口地址: POST /coder/sysLoginLog/update
接口描述: 修改系统登录日志信息(通常用于更新退出时间)
是否需要认证: 是
权限要求: system:loginlog:edit
请求参数:
{
"infoId": 1,
"loginName": "admin",
"userName": "管理员",
"userId": 1,
"loginStatus": "0",
"clientType": "WEB",
"deviceName": "Windows PC",
"loginIp": "127.0.0.1",
"loginAddress": "本地登录",
"browser": "Chrome 120.0.0.0",
"os": "Windows 10",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"loginTime": "2024-07-05 10:00:00",
"logoutTime": "2024-07-05 11:00:00",
"sessionDuration": 3600,
"failureReason": "",
"remark": "正常退出"
}
请求参数说明:
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
| infoId | Long | 是 | 日志ID | 必须是有效的日志ID |
| logoutTime | String | 否 | 退出时间 | 时间格式 |
| sessionDuration | Integer | 否 | 会话时长(秒) | 大于等于0 |
| 其他参数 | - | - | 同新增日志 | - |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": "修改成功",
"traceId": "trace-123456"
}
调用示例:
curl -X POST \
http://localhost:18099/coder/sysLoginLog/update \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token-value" \
-d '{
"infoId": 1,
"logoutTime": "2024-07-05 11:00:00",
"sessionDuration": 3600,
"remark": "正常退出"
}'
6. 删除登录日志
接口地址: POST /coder/sysLoginLog/deleteById/{id}
接口描述: 根据ID删除登录日志
是否需要认证: 是
权限要求: system:loginlog:remove
路径参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 是 | 日志ID |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": "删除成功",
"traceId": "trace-123456"
}
调用示例:
curl -X POST \
http://localhost:18099/coder/sysLoginLog/deleteById/1 \
-H "Authorization: Bearer your-token-value"
7. 批量删除登录日志
接口地址: POST /coder/sysLoginLog/batchDelete
接口描述: 批量删除登录日志
是否需要认证: 是
权限要求: system:loginlog:remove
请求参数:
{
"ids": [1, 2, 3]
}
请求参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | Long[] | 是 | 日志ID数组 |
响应示例:
{
"status": 200,
"msg": "SUCCESS",
"data": "删除成功",
"traceId": "trace-123456"
}
调用示例:
curl -X POST \
http://localhost:18099/coder/sysLoginLog/batchDelete \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token-value" \
-d '{
"ids": [1, 2, 3]
}'
登录日志字段说明
基础字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| infoId | Long | 日志ID | 1 |
| loginName | String | 登录账号 | admin |
| userName | String | 用户姓名 | 管理员 |
| userId | Long | 用户ID | 1 |
状态字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| loginStatus | String | 登录状态 | 0-成功 1-失败 |
| clientType | String | 客户端类型 | WEB, MOBILE, API |
| deviceName | String | 设备名称 | Windows PC |
网络字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| loginIp | String | 登录IP地址 | 127.0.0.1 |
| loginAddress | String | 登录地理位置 | 本地登录 |
| userAgent | String | 用户代理字符串 | Mozilla/5.0... |
环境字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| browser | String | 浏览器信息 | Chrome 120.0.0.0 |
| os | String | 操作系统 | Windows 10 |
时间字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| loginTime | String | 登录时间 | 2024-07-05 10:00:00 |
| logoutTime | String | 退出时间 | 2024-07-05 11:00:00 |
| sessionDuration | Integer | 会话时长(秒) | 3600 |
其他字段
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| failureReason | String | 失败原因 | 密码错误 |
| remark | String | 备注信息 | 登录成功 |
数据字典
登录状态 (loginStatus)
| 值 | 说明 |
|---|---|
| 0 | 登录成功 |
| 1 | 登录失败 |
客户端类型 (clientType)
| 值 | 说明 |
|---|---|
| WEB | 网页端 |
| MOBILE | 移动端 |
| API | 接口调用 |
| DESKTOP | 桌面应用 |
| 微信小程序 |
常见失败原因
| 失败原因 | 说明 |
|---|---|
| 用户不存在 | 登录账号不存在 |
| 密码错误 | 登录密码不正确 |
| 验证码错误 | 验证码输入错误 |
| 账号被禁用 | 用户账号被禁用 |
| 账号被锁定 | 用户账号被锁定 |
| IP被限制 | 登录IP被限制 |
| 设备被限制 | 登录设备被限制 |
| 会话过期 | 用户会话已过期 |
日志记录机制
1. 自动记录
@Component
@Slf4j
public class LoginLogService {
/**
* 记录登录成功日志
*/
public void recordLoginSuccess(String loginName, HttpServletRequest request) {
try {
SysLoginLog loginLog = new SysLoginLog();
// 基础信息
loginLog.setLoginName(loginName);
loginLog.setUserId(getCurrentUserId(loginName));
loginLog.setUserName(getCurrentUserName(loginName));
loginLog.setLoginStatus("0");
loginLog.setLoginTime(LocalDateTime.now());
// 网络信息
loginLog.setLoginIp(getClientIP(request));
loginLog.setLoginAddress(getAddressByIP(loginLog.getLoginIp()));
// 客户端信息
loginLog.setClientType(getClientType(request));
loginLog.setUserAgent(request.getHeader("User-Agent"));
loginLog.setBrowser(getBrowserInfo(request));
loginLog.setOs(getOSInfo(request));
loginLog.setDeviceName(getDeviceName(request));
// 其他信息
loginLog.setRemark("登录成功");
loginLog.setCreateBy("system");
loginLog.setCreateTime(LocalDateTime.now());
// 保存日志
sysLoginLogMapper.insert(loginLog);
} catch (Exception e) {
log.error("记录登录成功日志失败", e);
}
}
/**
* 记录登录失败日志
*/
public void recordLoginFailure(String loginName, String failureReason, HttpServletRequest request) {
try {
SysLoginLog loginLog = new SysLoginLog();
// 基础信息
loginLog.setLoginName(loginName);
loginLog.setLoginStatus("1");
loginLog.setLoginTime(LocalDateTime.now());
loginLog.setFailureReason(failureReason);
// 网络信息
loginLog.setLoginIp(getClientIP(request));
loginLog.setLoginAddress(getAddressByIP(loginLog.getLoginIp()));
// 客户端信息
loginLog.setClientType(getClientType(request));
loginLog.setUserAgent(request.getHeader("User-Agent"));
loginLog.setBrowser(getBrowserInfo(request));
loginLog.setOs(getOSInfo(request));
loginLog.setDeviceName(getDeviceName(request));
// 其他信息
loginLog.setRemark("登录失败:" + failureReason);
loginLog.setCreateBy("system");
loginLog.setCreateTime(LocalDateTime.now());
// 保存日志
sysLoginLogMapper.insert(loginLog);
} catch (Exception e) {
log.error("记录登录失败日志失败", e);
}
}
/**
* 记录退出登录日志
*/
public void recordLogout(String loginName, LocalDateTime loginTime) {
try {
// 查找对应的登录日志
SysLoginLog loginLog = sysLoginLogMapper.selectOne(
new LambdaQueryWrapper<SysLoginLog>()
.eq(SysLoginLog::getLoginName, loginName)
.eq(SysLoginLog::getLoginTime, loginTime)
.eq(SysLoginLog::getLoginStatus, "0")
.isNull(SysLoginLog::getLogoutTime)
.last("ORDER BY create_time DESC LIMIT 1")
);
if (loginLog != null) {
LocalDateTime logoutTime = LocalDateTime.now();
long sessionDuration = Duration.between(loginLog.getLoginTime(), logoutTime).getSeconds();
loginLog.setLogoutTime(logoutTime);
loginLog.setSessionDuration((int) sessionDuration);
loginLog.setRemark("正常退出");
loginLog.setUpdateBy("system");
loginLog.setUpdateTime(LocalDateTime.now());
sysLoginLogMapper.updateById(loginLog);
}
} catch (Exception e) {
log.error("记录退出登录日志失败", e);
}
}
/**
* 获取客户端IP
*/
private String getClientIP(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/**
* 解析浏览器信息
*/
private String getBrowserInfo(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
if (userAgent == null) {
return "Unknown";
}
if (userAgent.contains("Chrome")) {
return "Chrome";
} else if (userAgent.contains("Firefox")) {
return "Firefox";
} else if (userAgent.contains("Safari")) {
return "Safari";
} else if (userAgent.contains("Edge")) {
return "Edge";
} else if (userAgent.contains("IE")) {
return "Internet Explorer";
} else {
return "Other";
}
}
/**
* 解析操作系统信息
*/
private String getOSInfo(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
if (userAgent == null) {
return "Unknown";
}
if (userAgent.contains("Windows NT 10.0")) {
return "Windows 10";
} else if (userAgent.contains("Windows NT 6.3")) {
return "Windows 8.1";
} else if (userAgent.contains("Windows NT 6.2")) {
return "Windows 8";
} else if (userAgent.contains("Windows NT 6.1")) {
return "Windows 7";
} else if (userAgent.contains("Mac OS X")) {
return "Mac OS";
} else if (userAgent.contains("Linux")) {
return "Linux";
} else if (userAgent.contains("Android")) {
return "Android";
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) {
return "iOS";
} else {
return "Other";
}
}
}
2. 异步处理
@Service
public class AsyncLoginLogService {
@Async("taskExecutor")
public void recordLoginLogAsync(SysLoginLog loginLog) {
try {
// 异步记录登录日志
sysLoginLogMapper.insert(loginLog);
// 更新用户登录信息
updateUserLoginInfo(loginLog);
// 检查异常登录
checkAbnormalLogin(loginLog);
} catch (Exception e) {
log.error("异步记录登录日志失败", e);
}
}
/**
* 检查异常登录
*/
private void checkAbnormalLogin(SysLoginLog loginLog) {
// 检查IP异常
if (isAbnormalIP(loginLog.getLoginIp(), loginLog.getLoginName())) {
sendSecurityAlert("检测到异常IP登录", loginLog);
}
// 检查设备异常
if (isAbnormalDevice(loginLog.getUserAgent(), loginLog.getLoginName())) {
sendSecurityAlert("检测到异常设备登录", loginLog);
}
// 检查时间异常
if (isAbnormalTime(loginLog.getLoginTime(), loginLog.getLoginName())) {
sendSecurityAlert("检测到异常时间登录", loginLog);
}
}
}
统计分析功能
1. 登录统计
/**
* 登录统计服务
*/
@Service
public class LoginStatisticsService {
/**
* 获取登录成功率统计
*/
public LoginSuccessRateVO getLoginSuccessRate(String beginTime, String endTime) {
// 查询总登录次数
long totalCount = sysLoginLogMapper.selectCount(
new LambdaQueryWrapper<SysLoginLog>()
.between(SysLoginLog::getLoginTime, beginTime, endTime)
);
// 查询成功登录次数
long successCount = sysLoginLogMapper.selectCount(
new LambdaQueryWrapper<SysLoginLog>()
.eq(SysLoginLog::getLoginStatus, "0")
.between(SysLoginLog::getLoginTime, beginTime, endTime)
);
// 计算成功率
double successRate = totalCount > 0 ? (double) successCount / totalCount * 100 : 0;
return LoginSuccessRateVO.builder()
.totalCount(totalCount)
.successCount(successCount)
.failureCount(totalCount - successCount)
.successRate(successRate)
.build();
}
/**
* 获取每日登录统计
*/
public List<DailyLoginStatVO> getDailyLoginStat(String beginTime, String endTime) {
return sysLoginLogMapper.selectDailyLoginStat(beginTime, endTime);
}
/**
* 获取客户端类型统计
*/
public List<ClientTypeStatVO> getClientTypeStat(String beginTime, String endTime) {
return sysLoginLogMapper.selectClientTypeStat(beginTime, endTime);
}
}
2. 安全监控
/**
* 安全监控服务
*/
@Service
public class SecurityMonitorService {
/**
* 检测暴力破解
*/
public void detectBruteForce() {
// 查询5分钟内失败次数超过5次的IP
List<String> suspiciousIPs = sysLoginLogMapper.selectSuspiciousIPs();
for (String ip : suspiciousIPs) {
// 加入黑名单
addToBlacklist(ip);
// 发送告警
sendSecurityAlert("检测到暴力破解攻击", ip);
}
}
/**
* 检测异地登录
*/
public void detectRemoteLogin() {
// 查询用户最近登录地址
List<SysLoginLog> recentLogins = sysLoginLogMapper.selectRecentLogins();
for (SysLoginLog loginLog : recentLogins) {
String lastLoginAddress = getLastLoginAddress(loginLog.getLoginName());
if (!loginLog.getLoginAddress().equals(lastLoginAddress)) {
// 发送异地登录通知
sendRemoteLoginNotification(loginLog);
}
}
}
}
错误码说明
| 错误码 | 错误信息 | 说明 |
|---|---|---|
| 400 | 登录账号不能为空 | 登录账号为空 |
| 400 | 登录状态不能为空 | 登录状态为空 |
| 400 | 登录时间不能为空 | 登录时间为空 |
| 400 | 登录IP不能为空 | 登录IP为空 |
| 400 | 日志不存在 | 日志ID不存在 |
| 400 | 时间格式错误 | 时间格式不正确 |
| 401 | 当前会话未登录 | 未登录或Token无效 |
| 403 | 权限不足 | 没有相应的操作权限 |
| 500 | 日志记录失败 | 日志保存失败 |
| 500 | 系统异常 | 服务器内部错误 |
日志清理策略
1. 定时清理
@Component
public class LoginLogCleanupTask {
/**
* 每天凌晨2点清理30天前的登录日志
*/
@Scheduled(cron = "0 0 2 * * ?")
public void cleanupOldLogs() {
try {
LocalDateTime cutoffTime = LocalDateTime.now().minusDays(30);
int deletedCount = sysLoginLogMapper.delete(
new LambdaQueryWrapper<SysLoginLog>()
.lt(SysLoginLog::getCreateTime, cutoffTime)
);
log.info("清理登录日志完成,删除{}条记录", deletedCount);
} catch (Exception e) {
log.error("清理登录日志失败", e);
}
}
/**
* 归档登录日志
*/
@Scheduled(cron = "0 0 1 1 * ?") // 每月1号凌晨1点执行
public void archiveLogs() {
try {
LocalDateTime lastMonth = LocalDateTime.now().minusMonths(1);
// 归档上个月的日志到历史表
sysLoginLogMapper.archiveLogsToHistory(lastMonth);
log.info("归档登录日志完成");
} catch (Exception e) {
log.error("归档登录日志失败", e);
}
}
}
2. 日志配置
# application.yml
logging:
login-log:
# 是否启用登录日志
enabled: true
# 日志保留天数
retention-days: 90
# 是否记录成功登录
log-success: true
# 是否记录失败登录
log-failure: true
# 是否异步记录
async: true
# 是否启用IP地址解析
resolve-address: true
# 是否启用安全监控
security-monitor: true
使用建议
1. 日志管理
- 合理保留期: 根据业务需要设置日志保留期
- 定期清理: 定期清理过期日志释放存储空间
- 分表存储: 大量日志可考虑按月分表存储
- 归档备份: 重要日志定期归档备份
2. 安全监控
- 实时监控: 实时监控异常登录行为
- 告警机制: 建立完善的安全告警机制
- 自动响应: 对可疑行为自动采取防护措施
- 定期分析: 定期分析登录日志发现安全趋势
3. 性能优化
- 异步记录: 使用异步方式记录日志避免影响登录性能
- 批量处理: 批量处理日志数据提高效率
- 索引优化: 为查询字段建立合适索引
- 分页查询: 大数据量查询使用分页
4. 隐私保护
- 敏感信息: 不记录密码等敏感信息
- 数据脱敏: 对部分信息进行脱敏处理
- 访问控制: 严格控制日志访问权限
- 合规要求: 遵守相关数据保护法规
注意事项
- 日志完整性: 确保登录日志记录的完整性和准确性
- 性能影响: 日志记录不应影响用户登录体验
- 存储空间: 注意日志存储空间的管理和清理
- 安全防护: 防止日志被恶意篡改或删除
- 隐私保护: 不记录用户密码等敏感信息
- 异常处理: 日志记录失败不应影响正常业务
- 监控告警: 建立异常登录的监控告警机制
- 数据备份: 重要日志数据需要备份
- 合规要求: 遵守相关法律法规对日志的要求
- 访问审计: 对日志访问行为也要进行审计