@@ -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 |