feat: 优化日志记录,避免在登录和错误处理时输出敏感信息

This commit is contained in:
zyh
2025-09-09 20:16:34 +08:00
parent 3a09a4469b
commit aaee312662
5 changed files with 61 additions and 28 deletions

View File

@@ -28,7 +28,8 @@ public class AuthController {
@PostMapping("/login") @PostMapping("/login")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
public Mono<LoginResponse> login(@Valid @RequestBody LoginRequest req) { public Mono<LoginResponse> login(@Valid @RequestBody LoginRequest req) {
log.info("/api/auth/login called username={}", req.getUsername()); // Avoid logging raw usernames at info level
log.debug("/api/auth/login called");
return authService.login(req); return authService.login(req);
} }

View File

@@ -58,7 +58,7 @@ public class QrProxyController {
// 通过codeNo查询machineId // 通过codeNo查询machineId
String machineId = linkStatusService.getMechainIdByCode(codeNo); String machineId = linkStatusService.getMechainIdByCode(codeNo);
if (machineId == null) { if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo); log.warn("无法找到codeNo对应的machineId");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
} }
@@ -70,11 +70,11 @@ public class QrProxyController {
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=qr.png") .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=qr.png")
.body(bytes)) .body(bytes))
.onErrorResume(WebClientResponseException.NotFound.class, ex -> { .onErrorResume(WebClientResponseException.NotFound.class, ex -> {
log.warn("图片不存在: path={}", path); log.warn("图片不存在");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
}) })
.onErrorResume(WebClientResponseException.class, ex -> { .onErrorResume(WebClientResponseException.class, ex -> {
log.warn("获取图片失败: path={}, status={}, error={}", path, ex.getStatusCode(), ex.getMessage()); log.warn("获取图片失败: status={}, error={}", ex.getStatusCode(), ex.getMessage());
return Mono.just(ResponseEntity.status(ex.getStatusCode()).build()); return Mono.just(ResponseEntity.status(ex.getStatusCode()).build());
}); });
} }
@@ -86,7 +86,7 @@ public class QrProxyController {
.flatMap(linkStatus -> { .flatMap(linkStatus -> {
String machineId = linkStatus.getMachineId(); String machineId = linkStatus.getMachineId();
if (machineId == null) { if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo); log.warn("无法找到codeNo对应的machineId");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
} }
@@ -98,16 +98,16 @@ public class QrProxyController {
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=homepage.png") .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=homepage.png")
.body(bytes)) .body(bytes))
.onErrorResume(WebClientResponseException.NotFound.class, ex -> { .onErrorResume(WebClientResponseException.NotFound.class, ex -> {
log.warn("图片不存在: path={}", path); log.warn("图片不存在");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
}) })
.onErrorResume(WebClientResponseException.class, ex -> { .onErrorResume(WebClientResponseException.class, ex -> {
log.warn("获取图片失败: path={}, status={}, error={}", path, ex.getStatusCode(), ex.getMessage()); log.warn("获取图片失败: status={}, error={}", ex.getStatusCode(), ex.getMessage());
return Mono.just(ResponseEntity.status(ex.getStatusCode()).build()); return Mono.just(ResponseEntity.status(ex.getStatusCode()).build());
}); });
}) })
.onErrorResume(Exception.class, ex -> { .onErrorResume(Exception.class, ex -> {
log.error("获取链接状态失败: codeNo={}, error={}", codeNo, ex.getMessage()); log.error("获取链接状态失败: {}", ex.getMessage());
return createInternalServerErrorResponseMono(); return createInternalServerErrorResponseMono();
}); });
} }
@@ -119,7 +119,7 @@ public class QrProxyController {
.flatMap(linkStatus -> { .flatMap(linkStatus -> {
String machineId = linkStatus.getMachineId(); String machineId = linkStatus.getMachineId();
if (machineId == null) { if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo); log.warn("无法找到codeNo对应的machineId");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
} }
@@ -131,16 +131,16 @@ public class QrProxyController {
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=first-reward.png") .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=first-reward.png")
.body(bytes)) .body(bytes))
.onErrorResume(WebClientResponseException.NotFound.class, ex -> { .onErrorResume(WebClientResponseException.NotFound.class, ex -> {
log.warn("图片不存在: path={}", path); log.warn("图片不存在");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
}) })
.onErrorResume(WebClientResponseException.class, ex -> { .onErrorResume(WebClientResponseException.class, ex -> {
log.warn("获取图片失败: path={}, status={}, error={}", path, ex.getStatusCode(), ex.getMessage()); log.warn("获取图片失败: status={}, error={}", ex.getStatusCode(), ex.getMessage());
return Mono.just(ResponseEntity.status(ex.getStatusCode()).build()); return Mono.just(ResponseEntity.status(ex.getStatusCode()).build());
}); });
}) })
.onErrorResume(Exception.class, ex -> { .onErrorResume(Exception.class, ex -> {
log.error("获取链接状态失败: codeNo={}, error={}", codeNo, ex.getMessage()); log.error("获取链接状态失败: {}", ex.getMessage());
return createInternalServerErrorResponseMono(); return createInternalServerErrorResponseMono();
}); });
} }
@@ -152,7 +152,7 @@ public class QrProxyController {
.flatMap(linkStatus -> { .flatMap(linkStatus -> {
String machineId = linkStatus.getMachineId(); String machineId = linkStatus.getMachineId();
if (machineId == null) { if (machineId == null) {
log.warn("无法找到codeNo对应的machineId: {}", codeNo); log.warn("无法找到codeNo对应的machineId");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
} }
@@ -164,16 +164,16 @@ public class QrProxyController {
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=mid-reward.png") .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=mid-reward.png")
.body(bytes)) .body(bytes))
.onErrorResume(WebClientResponseException.NotFound.class, ex -> { .onErrorResume(WebClientResponseException.NotFound.class, ex -> {
log.warn("图片不存在: path={}", path); log.warn("图片不存在");
return createNotFoundResponseMono(); return createNotFoundResponseMono();
}) })
.onErrorResume(WebClientResponseException.class, ex -> { .onErrorResume(WebClientResponseException.class, ex -> {
log.warn("获取图片失败: path={}, status={}, error={}", path, ex.getStatusCode(), ex.getMessage()); log.warn("获取图片失败: status={}, error={}", ex.getStatusCode(), ex.getMessage());
return Mono.just(ResponseEntity.status(ex.getStatusCode()).build()); return Mono.just(ResponseEntity.status(ex.getStatusCode()).build());
}); });
}) })
.onErrorResume(Exception.class, ex -> { .onErrorResume(Exception.class, ex -> {
log.error("获取链接状态失败: codeNo={}, error={}", codeNo, ex.getMessage()); log.error("获取链接状态失败: {}", ex.getMessage());
return createInternalServerErrorResponseMono(); return createInternalServerErrorResponseMono();
}); });
} }
@@ -315,4 +315,3 @@ public class QrProxyController {
} }
} }

View File

@@ -0,0 +1,22 @@
package com.gameplatform.server.mapper.lock;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface LockMapper {
/**
* Try to acquire a named MySQL advisory lock. Returns 1 if acquired, 0 if timeout, NULL on error.
*/
@Select("SELECT GET_LOCK(#{name}, #{timeoutSeconds})")
Integer getLock(@Param("name") String name, @Param("timeoutSeconds") int timeoutSeconds);
/**
* Release the named advisory lock. Returns 1 if released, 0 if not held, NULL on error.
*/
@Select("SELECT RELEASE_LOCK(#{name})")
Integer releaseLock(@Param("name") String name);
}

View File

@@ -124,7 +124,12 @@ public class MachineCooldownService {
*/ */
@Transactional @Transactional
public void addMachineToCooldown(String machineId, String reason) { public void addMachineToCooldown(String machineId, String reason) {
addMachineToCooldown(machineId, reason, null); try {
addMachineToCooldown(machineId, reason, null);
} catch (RuntimeException ex) {
// 当上层已完成占用(或并发占用导致唯一键冲突)时,吞掉异常以便上层逻辑继续
log.warn("添加冷却记录失败或已存在machineId={},原因={}(忽略)", machineId, ex.getMessage());
}
} }
/** /**
@@ -144,24 +149,24 @@ public class MachineCooldownService {
LocalDateTime cooldownEndTime = now.plusMinutes(COOLDOWN_MINUTES); LocalDateTime cooldownEndTime = now.plusMinutes(COOLDOWN_MINUTES);
try { try {
// 先移除该设备现有的活跃冷却记录 // 先移除该设备现有的活跃冷却记录(若存在非并发场景下的残留)
machineCooldownMapper.removeMachineCooldown(machineId); machineCooldownMapper.removeMachineCooldown(machineId);
// 创建新的冷却记录 // 创建新的冷却记录(依赖数据库唯一约束防止并发重复占用)
MachineCooldown cooldown = new MachineCooldown( MachineCooldown cooldown = new MachineCooldown(
machineId, now, cooldownEndTime, reason, linkTaskId); machineId, now, cooldownEndTime, reason, linkTaskId);
machineCooldownMapper.insert(cooldown); machineCooldownMapper.insert(cooldown);
// 更新缓存 // 更新缓存
machineCooldownCache.put(machineId, now); machineCooldownCache.put(machineId, now);
log.info("机器{}已加入冷却队列,原因:{},冷却时间:{}分钟,冷却结束时间:{},关联任务:{}", log.info("机器{}已加入冷却队列,原因:{},冷却时间:{}分钟,冷却结束时间:{},关联任务:{}",
machineId, reason, COOLDOWN_MINUTES, cooldownEndTime, linkTaskId); machineId, reason, COOLDOWN_MINUTES, cooldownEndTime, linkTaskId);
} catch (Exception e) { } catch (Exception e) {
log.error("将机器{}加入冷却队列失败:{}", machineId, e.getMessage(), e); log.error("将机器{}加入冷却队列失败:{}", machineId, e.getMessage(), e);
// 即使数据库操作失败,也要更新缓存作为备 // 抛出异常,交由上层流程回滚/重试,避免同一设备被并发占
machineCooldownCache.put(machineId, now); throw new RuntimeException("设备正忙或分配冲突,请稍后重试");
} }
} }
@@ -290,4 +295,4 @@ public class MachineCooldownService {
return List.of(); return List.of();
} }
} }
} }

View File

@@ -0,0 +1,6 @@
-- Add a unique constraint to ensure a device has at most one ACTIVE cooldown record at a time.
-- This helps prevent concurrent assignments of the same machine to multiple links.
ALTER TABLE machine_cooldown
ADD UNIQUE KEY ux_machine_cooldown_active (machine_id, status);