diff --git a/src/main/java/com/lion/config/UserLoginRecordCache.java b/src/main/java/com/lion/config/UserLoginRecordCache.java new file mode 100644 index 0000000..73ac353 --- /dev/null +++ b/src/main/java/com/lion/config/UserLoginRecordCache.java @@ -0,0 +1,177 @@ +package com.lion.config; + +import com.lion.entity.LoginUserInfo; +import com.lion.util.PropertiesUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +// zhao@20230427: 用户登录失败记录缓存 +public final class UserLoginRecordCache +{ + private static final Logger logger = LoggerFactory.getLogger(UserLoginRecordCache.class); + public static final Map cache = new HashMap<>(); + + private static Integer FAIL_COUNT_LIMIT; + private static Integer FAIL_LIMIT_TIME; + private static boolean inited = false; + + private static void Init() + { + if(inited) + return; + FAIL_COUNT_LIMIT = ParseInt(PropertiesUtil.getProperty("USER_LOGIN.FAIL_COUNT_LIMIT")); + FAIL_LIMIT_TIME = ParseInt(PropertiesUtil.getProperty("USER_LOGIN.FAIL_LIMIT_TIME")); + inited = true; + } + + private static int ParseInt(String val) + { + if(StringUtils.isEmpty(val)) + return 0; + try + { + return Integer.parseInt(val); + } + catch(Exception e) + { + e.printStackTrace(); + return 0; + } + } + + public static String BeginUserLogin(String userId) + { + Init(); + synchronized(cache) { + long time; + LoginUserInfo user; + + if(FAIL_COUNT_LIMIT <= 0) + return null; + + if(!cache.containsKey(userId)) // 未登录过 + { + user = new LoginUserInfo(); + user.setUserAccount(userId); + user.LoginFailCount(0); + user.LoginFailPunishStartTime(0); + cache.put(userId, user); + LOG("用户 {} 从未登录", userId); + return null; + } + else + user = cache.get(userId); + + if(FAIL_LIMIT_TIME > 0) + { + time = System.currentTimeMillis(); + long loginFailPunishStartTime = user.LoginFailPunishStartTime(); + if(loginFailPunishStartTime > 0) // 锁定中 + { + if(time - loginFailPunishStartTime > FAIL_LIMIT_TIME * 1000) // 过了锁定时间 + { + LOG("用户 {} 登录锁定中且过了锁定时间: {} - {} > {}秒", userId, date_format(time), date_format(loginFailPunishStartTime), FAIL_LIMIT_TIME); + user.LoginFailPunishStartTime(0); + user.LoginFailCount(0); + return null; + } + else // 还在锁定时间 + { + LOG("用户 {} 登录锁定中: {} - {} <= {}秒", userId, date_format(time), date_format(loginFailPunishStartTime), FAIL_LIMIT_TIME); + return "该用户登录失败已超过限制次数, 锁定登录中"; + } + } + else // 未锁定 + { + if(user.LoginFailCount() >= FAIL_COUNT_LIMIT) // 超过限制 + { + user.LoginFailPunishStartTime(time); + LOG("用户 {} 锁定登录: {}", userId, date_format(time)); + return "该用户登录失败已超过限制次数, 将锁定登录"; + } + else // 还有次数 + { + LOG("用户 {} 允许登录: {} < {}", userId, user.LoginFailCount(), FAIL_COUNT_LIMIT); + return null; + } + } + } + else + return null; + } + } + + public static boolean EndUserLogin(String userId, boolean success) + { + synchronized(cache) { + long time; + LoginUserInfo user; + + if(FAIL_COUNT_LIMIT <= 0) + return false; + user = cache.get(userId); + + if(success) // 登录成功清空失败记录 + { + LOG("用户 {} 登录成功", userId); + user.LoginFailPunishStartTime(0); + user.LoginFailCount(0); + return false; + } + + time = System.currentTimeMillis(); + + user.LoginFailCount(user.LoginFailCount() + 1); + if(FAIL_LIMIT_TIME > 0) + { + if(user.LoginFailCount() >= FAIL_COUNT_LIMIT) // 超过失败次数 + { + LOG("用户 {} 超出登录失败次数, 开始锁定: {} >= {}", userId, user.LoginFailCount(), FAIL_COUNT_LIMIT); + user.LoginFailPunishStartTime(time); // 锁定 + return true; + } + else + { + LOG("用户 {} 登录失败剩余次数 {}", userId, FAIL_COUNT_LIMIT - user.LoginFailCount()); + user.LoginFailPunishStartTime(0); // 不锁定 + return false; + } + } + else + { + user.LoginFailPunishStartTime(0); + if(user.LoginFailCount() >= FAIL_COUNT_LIMIT) + user.LoginFailCount(0); + return false; + } + } + } + + private static String date_format(long ts) + { + return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss-SSS"); + } + + private static void LOG(Object obj, Object...args) + { + if(null == obj) + logger.info("NULL"); + else + { + if(obj instanceof String) + { + logger.info((String)obj, args); + } + else + { + logger.info(obj.toString()); + } + } + } +} diff --git a/src/main/java/com/lion/entity/LoginUserInfo.java b/src/main/java/com/lion/entity/LoginUserInfo.java index a819e7c..b8c2b81 100644 --- a/src/main/java/com/lion/entity/LoginUserInfo.java +++ b/src/main/java/com/lion/entity/LoginUserInfo.java @@ -17,6 +17,22 @@ public class LoginUserInfo { private String userAccount ;//登录账号 private String roleid; private String orgid; + + // zhao@20230427 + private long loginFailPunishStartTime; // 登录失败惩罚开始时间 + private int loginFailCount; // 登录失败次数 + public long LoginFailPunishStartTime(long...l) + { + if(null != l && l.length > 0) + loginFailPunishStartTime = l[0]; + return loginFailPunishStartTime; + } + public int LoginFailCount(int...i) + { + if(null != i&& i.length > 0) + loginFailCount = i[0]; + return loginFailCount; + } public String getUserId() { return userId; diff --git a/src/main/java/com/lion/service/LoginService.java b/src/main/java/com/lion/service/LoginService.java index c0522e7..9b36a0e 100644 --- a/src/main/java/com/lion/service/LoginService.java +++ b/src/main/java/com/lion/service/LoginService.java @@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import com.lion.config.UserLoginRecordCache; import org.apache.log4j.Logger; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -77,6 +78,11 @@ public class LoginService { if(!code.equalsIgnoreCase(sessionCode)){ return Util.responseString("false","验证码错误"); } + + String validateResult = UserLoginRecordCache.BeginUserLogin(userName); + if(null != validateResult) + return Util.responseString("false", validateResult); + try { password = RasUtil.decrypt(password, private_key); } catch (Exception e) { @@ -91,6 +97,7 @@ public class LoginService { String userId=list.get(0).get("id").toString(); String roleId=list.get(0).get("roleid").toString();//角色id if(StringUtil.isEmpty(roleId)){ + UserLoginRecordCache.EndUserLogin(userName, false); return Util.responseString("false","该用户未分配角色,禁止登录"); } String cookie=MD5Util.StringInMd5(userId+TimeUtil.getNowTimeTwo()); @@ -109,14 +116,18 @@ public class LoginService { json.put("success","true"); json.put("message",cookie); json.put("orgid",list.get(0).get("orgid").toString()); + UserLoginRecordCache.EndUserLogin(userName, true); return json.toString(); }else{ + UserLoginRecordCache.EndUserLogin(userName, false); return Util.responseString("false","登录失败"); } }else{ + UserLoginRecordCache.EndUserLogin(userName, false); return Util.responseString("false","密码错误"); } } + UserLoginRecordCache.EndUserLogin(userName, false); return Util.responseString("false","用户名或密码不存在"); } diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties index 6610617..4061886 100644 --- a/src/main/resources/config.properties +++ b/src/main/resources/config.properties @@ -10,3 +10,7 @@ loginOut=http\://stage.huizhou.tydlxt.znxdcloud.com/api/user/logout public_key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyAtzfkvb0EMNrlFNF1uYKYdtyYOVYUMfqWnAvRbE9XZoDlNUKqvKM8VfoNHrLBAe247iZW37+SiPqj28fztBJbEvbDOGLN0lINSyRac7v+MwSuNWGJAFE4/beM3vZz/21MDmbCReBbUSr1OBnLvzi9cPPSkaSN7trh1j/wwC559pUdhvf0MieJKwi0Q6k3NVpafszQw86r96LXfrL4GVk4OvXXIOKwFyNBgRJkWROz9L2DDJMebs4P4SJlLq+EeGRfAaOHzI8tEr/cOeiowM54z2g5BeM5xH28PpMlsx3FYXKwL+hJHs0+AQYyCt3ooPHJ4Q+7quVMOIB4Q+ATP6OQIDAQAB #ras秘钥 private_key=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIC3N+S9vQQw2uUU0XW5gph23Jg5VhQx+pacC9FsT1dmgOU1Qqq8ozxV+g0essEB7bjuJlbfv5KI+qPbx/O0ElsS9sM4Ys3SUg1LJFpzu/4zBK41YYkAUTj9t4ze9nP/bUwOZsJF4FtRKvU4Gcu/OL1w89KRpI3u2uHWP/DALnn2lR2G9/QyJ4krCLRDqTc1Wlp+zNDDzqv3otd+svgZWTg69dcg4rAXI0GBEmRZE7P0vYMMkx5uzg/hImUur4R4ZF8Bo4fMjy0Sv9w56KjAznjPaDkF4znEfbw+kyWzHcVhcrAv6EkezT4BBjIK3eig8cnhD7uq5Uw4gHhD4BM/o5AgMBAAECggEBAKbxB30KiXKPwscKii9H/QALSQ+2g98bSTz0SCiE6/F80vHBkKfbnW7+9AMdrAilm1rXLxA+bN1NgDcuNjRnmsJ9z3mYtgySsP1pAy0llrNYQWD9v0fYSKQ+lYWHNpcPxklege0VnHFe3yz5n1zRbTnyS4Fa39iYUfnQBTc/Kt1nrZTXqL8cHPMUHyD9sYG40JLSc5Cnr6yPRZF0DSB93tvobvqk5Ao8QiP7durVfgkKULUfNZY0ChNZ0DYcVfYqiVk2iyvtcqWLsyykjfCTuOSukniVyEDLH+PZH/8eGw5trCKfSMDY9J1cQX7oBcc+DpGI1jacvWWkicsrFE3/wlECgYEA5kGbtdhVl+wUIn3JfJKOBfa21LD6/0O9WPJqWLblF82rdlA6wfmV6rFtgpDbibT3pz1hgjKvTioXuJ4dbT0Hh4VOHIYJCQ8bcqQMfyWYPLDRzPFwaITGAwztFcZ8QoaAYqAz9FXASaBzJtCD4CmAdvFl9rug7Q88i9ZvyAsI+18CgYEA3mkg258UNDh/cXnhyDXVZ5gpEJsbSyaJK0gToFelDP7loj3vFsyOw6qYTqI8zygGxznrGxW2+QS7YebQWVdERCjwdRnbRqAIqecAJiqygCIs+9DO8PPj8733B7t9c9+FQlSSIv2DXk7ZwTWzpHa6XmkCCBd1Mr2gYE4dgaiPiWcCgYA0/9MEh1gkUP3NdqCjIOS58LDiwh30FbaGu73Iz8hpsziNUihEL9vXGqH3VLqDtvjuMM2590qstjmhkBt74nlSM1fobt2zjRunRqVtusyQ465W+xgBptYhK5+CzJ4bffQdP3zV98r754e53nMMbOEZ/7SVl1iSWFh6Y5B1Pj0CBwKBgBgbzTQBSm9esHHchFzvePKBy/HA3nSG+Nd4OKho17tQ5hNwIzqVceRD2b6sV2sdK57s6E9HS04y9RznqC4HB59b0LTg2KQPNOE16Q46Ep9RFQ820zfLOCpzrRNpf2/QCuHlyPGCCXtvkKsg5xgsxob1WwMFh+64H2pRDWHAo86BAoGAfyD3Is5t6i8AmL/0W2Iq6qgT8C6ArmkiMZ4eXYX2Ko1MtjjAsRAcNJooC9/fTsRgNRe1EtR7a4jBzBvOISET9VAyhbGvLVNjFXdIn1oF4EqHlBXmk/D/G48+KOx8g4Z7bwzYL3cLPydhbGDoO2MHIPJlS07j8xGAgoVQGBTnWfw= +# ??????????????, 0??? +USER_LOGIN.FAIL_COUNT_LIMIT=5 +# ??????????????????, 0??? +USER_LOGIN.FAIL_LIMIT_TIME=90