筆試題:
登陸安全的題目,如果你的系統登陸接口在被刷。我們要建立一個防刷系統。
根據登陸ip,30分鍾之內,只能請求30次登陸請求,如果超過這個限制,則整個ip限制登陸請求30分鍾
設計數據結構和實現代碼模擬分布式限流,多線程問題。不允許使用redis等。
設計思路:
這道題主要是設計兩個Map,
第一個Map,記錄每個IP及其登錄的時間,題目要求,30分鍾之內只能登錄30次,
所以Map的key為IP,value可以設計一個隊列,隊列長度30,隊列元素為每次的登錄時間。
第二個Map,記錄禁止登錄的IP及禁止開始時間,Map的key為IP,value為時間。
實現代碼如下:
/** * 限流器實現代碼 * 單例模式是為了保證一個JVM中只有一個限流器 */ public class LimitCache { private static LimitCache instance; // 記錄登錄的ip地址及每次登錄時間 private static HashMap<String, LinkedList<LocalDateTime>> loginMap = new HashMap<String, LinkedList<LocalDateTime>>(); // 記錄禁止登錄的ip地址及禁止開始時間 private static HashMap<String, LocalDateTime> forbiddenMap = new HashMap<String, LocalDateTime>(); // 私有化構造方法 private LimitCache() { } // 單例:雙重檢查模式 (DCL) public static synchronized LimitCache getInstance() { if (instance == null) { synchronized (LimitCache.class) { if (instance == null) { instance = new LimitCache(); } } } return instance; } public static HashMap<String, LinkedList<LocalDateTime>> getLoginMap() { return loginMap; } public static HashMap<String, LocalDateTime> getForbiddenMap() { return forbiddenMap; } }
/** * 模擬登陸 * */ @RestController public class LoginController { @GetMapping("/login") public String login(String ip) { String result = ""; LimitCache limitCache = LimitCache.getInstance(); LinkedList<LocalDateTime> queue = null; // 先判斷ip地址是否禁止登錄 LocalDateTime forbiddenTime = limitCache.getForbiddenMap().get(ip); if (forbiddenTime != null) { Long after = ChronoUnit.MINUTES.between(forbiddenTime, LocalDateTime.now()); if (after <= 30) { result = "當前時間=" + LocalDateTime.now() + " 上次禁止登錄時間= " + forbiddenTime + " 距上次被禁時間沒有超過30分鍾"; return result; } else { limitCache.getForbiddenMap().clear(); } } // 如果是首次登錄,則創建隊列 if (limitCache.getLoginMap().get(ip) == null) { queue = new LinkedList<LocalDateTime>(); } else { queue = limitCache.getLoginMap().get(ip); } // 登錄次數達到登錄次數上限 if (queue.size() == 30) { // 當前時間和隊列中最早的登錄時間比較 是否小於30分鍾 LocalDateTime now = LocalDateTime.now(); LocalDateTime firstLoginTime = queue.poll(); Long duration = ChronoUnit.MINUTES.between(firstLoginTime, now); if (duration <= 30) { result = "30分鍾內登錄超過30次,不允許登錄,30分鍾后再登錄"; // 禁止該IP登錄 limitCache.getLoginMap().clear(); limitCache.getForbiddenMap().put(ip, now); return result; } }
queue.offer(LocalDateTime.now()); limitCache.getLoginMap().put(ip, queue); result = ip + " 登錄時間=" + queue.getLast() + " 隊列長度=" + queue.size(); return result; } }