New file |
| | |
| | | package com.cl.util; |
| | | |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | // 使用ConcurrentHashMap作为临时存储 |
| | | |
| | | public class LoginAttemptService { |
| | | private static final int MAX_ATTEMPTS = 5; |
| | | private static final long LOCK_TIME_MS = 5 * 60 * 1000; // 5分钟 |
| | | |
| | | private final Map<String, LoginAttempt> attemptsCache = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * 是否被锁了 |
| | | */ |
| | | public boolean isLocked(String phone) { |
| | | LoginAttempt attempt = attemptsCache.get(phone); |
| | | return attempt != null |
| | | && attempt.getLockCount() >= MAX_ATTEMPTS |
| | | && System.currentTimeMillis() < attempt.getLastAttemptTime() + LOCK_TIME_MS; |
| | | } |
| | | |
| | | /** |
| | | * 添加密码错误记录 |
| | | */ |
| | | public void recordFailedAttempt(String phone) { |
| | | attemptsCache.compute(phone, (k, v) -> { |
| | | long now = System.currentTimeMillis(); |
| | | if (v == null) { |
| | | return new LoginAttempt(1, now); |
| | | } |
| | | |
| | | // 如果上次尝试超过锁定时间,则重置 |
| | | if (now - v.getLastAttemptTime() > LOCK_TIME_MS) { |
| | | return new LoginAttempt(1, now); |
| | | } |
| | | |
| | | return new LoginAttempt(v.getLockCount() + 1, now); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 清除 |
| | | */ |
| | | public void clearAttempts(String phone) { |
| | | attemptsCache.remove(phone); |
| | | } |
| | | |
| | | @Scheduled(fixedRate = 60 * 60 * 1000) // 每小时清理一次过期记录 |
| | | public void cleanupExpiredAttempts() { |
| | | long now = System.currentTimeMillis(); |
| | | attemptsCache.entrySet().removeIf(entry -> |
| | | now - entry.getValue().getLastAttemptTime() > LOCK_TIME_MS * 2 |
| | | ); |
| | | } |
| | | } |