| @@ -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<String, LoginUserInfo> 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()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -17,6 +17,22 @@ public class LoginUserInfo { | |||||
| private String userAccount ;//登录账号 | private String userAccount ;//登录账号 | ||||
| private String roleid; | private String roleid; | ||||
| private String orgid; | 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() { | public String getUserId() { | ||||
| return userId; | return userId; | ||||
| @@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
| import javax.servlet.http.HttpSession; | import javax.servlet.http.HttpSession; | ||||
| import com.lion.config.UserLoginRecordCache; | |||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||
| import org.json.JSONObject; | import org.json.JSONObject; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| @@ -77,6 +78,11 @@ public class LoginService { | |||||
| if(!code.equalsIgnoreCase(sessionCode)){ | if(!code.equalsIgnoreCase(sessionCode)){ | ||||
| return Util.responseString("false","验证码错误"); | return Util.responseString("false","验证码错误"); | ||||
| } | } | ||||
| String validateResult = UserLoginRecordCache.BeginUserLogin(userName); | |||||
| if(null != validateResult) | |||||
| return Util.responseString("false", validateResult); | |||||
| try { | try { | ||||
| password = RasUtil.decrypt(password, private_key); | password = RasUtil.decrypt(password, private_key); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| @@ -91,6 +97,7 @@ public class LoginService { | |||||
| String userId=list.get(0).get("id").toString(); | String userId=list.get(0).get("id").toString(); | ||||
| String roleId=list.get(0).get("roleid").toString();//角色id | String roleId=list.get(0).get("roleid").toString();//角色id | ||||
| if(StringUtil.isEmpty(roleId)){ | if(StringUtil.isEmpty(roleId)){ | ||||
| UserLoginRecordCache.EndUserLogin(userName, false); | |||||
| return Util.responseString("false","该用户未分配角色,禁止登录"); | return Util.responseString("false","该用户未分配角色,禁止登录"); | ||||
| } | } | ||||
| String cookie=MD5Util.StringInMd5(userId+TimeUtil.getNowTimeTwo()); | String cookie=MD5Util.StringInMd5(userId+TimeUtil.getNowTimeTwo()); | ||||
| @@ -109,14 +116,18 @@ public class LoginService { | |||||
| json.put("success","true"); | json.put("success","true"); | ||||
| json.put("message",cookie); | json.put("message",cookie); | ||||
| json.put("orgid",list.get(0).get("orgid").toString()); | json.put("orgid",list.get(0).get("orgid").toString()); | ||||
| UserLoginRecordCache.EndUserLogin(userName, true); | |||||
| return json.toString(); | return json.toString(); | ||||
| }else{ | }else{ | ||||
| UserLoginRecordCache.EndUserLogin(userName, false); | |||||
| return Util.responseString("false","登录失败"); | return Util.responseString("false","登录失败"); | ||||
| } | } | ||||
| }else{ | }else{ | ||||
| UserLoginRecordCache.EndUserLogin(userName, false); | |||||
| return Util.responseString("false","密码错误"); | return Util.responseString("false","密码错误"); | ||||
| } | } | ||||
| } | } | ||||
| UserLoginRecordCache.EndUserLogin(userName, false); | |||||
| return Util.responseString("false","用户名或密码不存在"); | return Util.responseString("false","用户名或密码不存在"); | ||||
| } | } | ||||
| @@ -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 | public_key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyAtzfkvb0EMNrlFNF1uYKYdtyYOVYUMfqWnAvRbE9XZoDlNUKqvKM8VfoNHrLBAe247iZW37+SiPqj28fztBJbEvbDOGLN0lINSyRac7v+MwSuNWGJAFE4/beM3vZz/21MDmbCReBbUSr1OBnLvzi9cPPSkaSN7trh1j/wwC559pUdhvf0MieJKwi0Q6k3NVpafszQw86r96LXfrL4GVk4OvXXIOKwFyNBgRJkWROz9L2DDJMebs4P4SJlLq+EeGRfAaOHzI8tEr/cOeiowM54z2g5BeM5xH28PpMlsx3FYXKwL+hJHs0+AQYyCt3ooPHJ4Q+7quVMOIB4Q+ATP6OQIDAQAB | ||||
| #ras秘钥 | #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= | 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 | |||||