mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
fix: add userId filter to audit search and login lockout logic
This commit is contained in:
+4
-2
@@ -34,19 +34,21 @@ public class AuditController {
|
||||
public PageResponse<AuditEventResponse> list(
|
||||
@RequestParam(required = false) String entityType,
|
||||
@RequestParam(required = false) Long entityId,
|
||||
@RequestParam(required = false) String userId,
|
||||
@RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
|
||||
@RequestParam(value = "size", defaultValue = "20") @Min(1) @Max(200) int size) {
|
||||
return auditService.page(entityType, entityId, page, size);
|
||||
return auditService.page(entityType, entityId, userId, page, size);
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
public ResponseEntity<Resource> exportAuditEvents(
|
||||
@RequestParam(required = false) String entityType,
|
||||
@RequestParam(required = false) Long entityId,
|
||||
@RequestParam(required = false) String userId,
|
||||
@RequestParam(required = false) String from,
|
||||
@RequestParam(required = false) String to) {
|
||||
|
||||
List<AuditEventResponse> events = auditService.searchAuditEvents(entityType, entityId, from, to);
|
||||
List<AuditEventResponse> events = auditService.searchAuditEvents(entityType, entityId, from, to, userId);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("时间,操作者,动作,实体类型,实体ID,摘要,详情\n");
|
||||
|
||||
+36
-1
@@ -1,7 +1,10 @@
|
||||
package cn.craftlabs.platform.api.auth;
|
||||
|
||||
import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttempt;
|
||||
import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttemptMapper;
|
||||
import cn.craftlabs.platform.api.security.JwtService;
|
||||
import cn.craftlabs.platform.api.security.PlatformRoles;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
@@ -20,10 +23,15 @@ public class AuthController {
|
||||
|
||||
private final JwtService jwtService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final PlatformLoginAttemptMapper loginAttemptMapper;
|
||||
private final HttpServletRequest request;
|
||||
|
||||
public AuthController(JwtService jwtService, PasswordEncoder passwordEncoder) {
|
||||
public AuthController(JwtService jwtService, PasswordEncoder passwordEncoder,
|
||||
PlatformLoginAttemptMapper loginAttemptMapper, HttpServletRequest request) {
|
||||
this.jwtService = jwtService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.loginAttemptMapper = loginAttemptMapper;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
@@ -31,6 +39,16 @@ public class AuthController {
|
||||
String user = body.getOrDefault("username", "");
|
||||
String pass = body.getOrDefault("password", "");
|
||||
|
||||
var recentQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformLoginAttempt.class)
|
||||
.eq(PlatformLoginAttempt::getUsername, user)
|
||||
.eq(PlatformLoginAttempt::getSuccess, false)
|
||||
.ge(PlatformLoginAttempt::getAttemptedAt, java.time.OffsetDateTime.now().minusMinutes(15));
|
||||
long recentFailed = loginAttemptMapper.selectCount(recentQuery);
|
||||
if (recentFailed >= 5) {
|
||||
throw new org.springframework.web.server.ResponseStatusException(
|
||||
org.springframework.http.HttpStatus.TOO_MANY_REQUESTS, "账户已临时锁定,请15分钟后重试");
|
||||
}
|
||||
|
||||
String role;
|
||||
String displayName;
|
||||
switch (user.toLowerCase()) {
|
||||
@@ -51,10 +69,22 @@ public class AuthController {
|
||||
displayName = "运营账号";
|
||||
break;
|
||||
default:
|
||||
PlatformLoginAttempt failedAttempt = new PlatformLoginAttempt();
|
||||
failedAttempt.setUsername(user);
|
||||
failedAttempt.setSuccess(false);
|
||||
failedAttempt.setIpAddress(request.getRemoteAddr());
|
||||
failedAttempt.setAttemptedAt(java.time.OffsetDateTime.now());
|
||||
loginAttemptMapper.insert(failedAttempt);
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials");
|
||||
}
|
||||
|
||||
if (!pass.equals(user.toLowerCase())) {
|
||||
PlatformLoginAttempt failedAttempt = new PlatformLoginAttempt();
|
||||
failedAttempt.setUsername(user);
|
||||
failedAttempt.setSuccess(false);
|
||||
failedAttempt.setIpAddress(request.getRemoteAddr());
|
||||
failedAttempt.setAttemptedAt(java.time.OffsetDateTime.now());
|
||||
loginAttemptMapper.insert(failedAttempt);
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials");
|
||||
}
|
||||
|
||||
@@ -90,6 +120,11 @@ public class AuthController {
|
||||
result.put("roles", List.of(role));
|
||||
result.put("displayName", displayName);
|
||||
result.put("permissions", permissions);
|
||||
|
||||
var clearQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformLoginAttempt.class)
|
||||
.eq(PlatformLoginAttempt::getUsername, user);
|
||||
loginAttemptMapper.delete(clearQuery);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -47,10 +47,19 @@ public class AuditService {
|
||||
auditEventMapper.insert(e);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<AuditEventResponse> searchAuditEvents(
|
||||
String entityType, Long entityId, String from, String to, String userId) {
|
||||
LambdaQueryWrapper<PlatformAuditEvent> q = buildQuery(entityType, entityId, from, to, userId);
|
||||
return auditEventMapper.selectList(q).stream()
|
||||
.map(this::toResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageResponse<AuditEventResponse> page(
|
||||
String entityType, Long entityId, int page, int size) {
|
||||
LambdaQueryWrapper<PlatformAuditEvent> q = buildQuery(entityType, entityId, null, null);
|
||||
String entityType, Long entityId, String userId, int page, int size) {
|
||||
LambdaQueryWrapper<PlatformAuditEvent> q = buildQuery(entityType, entityId, null, null, userId);
|
||||
Page<PlatformAuditEvent> mpPage = new Page<>(page + 1L, size);
|
||||
auditEventMapper.selectPage(mpPage, q);
|
||||
List<AuditEventResponse> content =
|
||||
@@ -58,17 +67,8 @@ public class AuditService {
|
||||
return new PageResponse<>(content, mpPage.getTotal(), page, size);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<AuditEventResponse> searchAuditEvents(
|
||||
String entityType, Long entityId, String from, String to) {
|
||||
LambdaQueryWrapper<PlatformAuditEvent> q = buildQuery(entityType, entityId, from, to);
|
||||
return auditEventMapper.selectList(q).stream()
|
||||
.map(this::toResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<PlatformAuditEvent> buildQuery(
|
||||
String entityType, Long entityId, String from, String to) {
|
||||
String entityType, Long entityId, String from, String to, String userId) {
|
||||
LambdaQueryWrapper<PlatformAuditEvent> q =
|
||||
Wrappers.lambdaQuery(PlatformAuditEvent.class)
|
||||
.orderByDesc(PlatformAuditEvent::getId);
|
||||
@@ -84,6 +84,9 @@ public class AuditService {
|
||||
if (to != null && !to.isBlank()) {
|
||||
q.le(PlatformAuditEvent::getCreatedAt, OffsetDateTime.parse(to + "T23:59:59Z"));
|
||||
}
|
||||
if (userId != null && !userId.isBlank()) {
|
||||
q.eq(PlatformAuditEvent::getActorUserId, userId.trim());
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user