Commit 170ab16e authored by 技术部-任文超's avatar 技术部-任文超

安全策略升级:“单IP在1小时内登陆成功次数限制为40次 超过这个值 对这个ip的登陆操作封禁 30分钟”

parent 59cb0015
...@@ -8,30 +8,15 @@ public interface Constants { ...@@ -8,30 +8,15 @@ public interface Constants {
// zero fill with 4 chars... // zero fill with 4 chars...
String ZERO_FILL_TEMPLATE = "%04d"; String ZERO_FILL_TEMPLATE = "%04d";
String PASSWORD_SALT = "_lkb"; String PASSWORD_SALT = "_lkb";
String IMAGE_CAPTCHA_KEY = "img_captcha:"; String IMAGE_CAPTCHA_KEY = "img_captcha:";
String REDIS_CAPTCHA_KEY = "auth:"; String REDIS_CAPTCHA_KEY = "auth:";
String CONFIG_CAPTCHA = "cfg_captcha_%";
// app 后端白名单
String CONFIG_CAPTCHA_WHITEIP_LIST = "cfg_captcha_white_ip_appbackend";
// 每个 IP 每分钟 captcha 限制
String CONFIG_CAPTCHA_PERIP_PERMIN = "cfg_captcha_per_ip_per_min";
// 是否启用万能验证码
String CONFIG_CAPTCHA_MAGIC_CODE_ENABLED = "cfg_captcha_magic_code_enabled";
String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_"; String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_";
String REDIS_PREFIX_VERIFICATION_VOICE_CODE = "verificationCode_voice_";
//新版短信验证码计数 //新版短信验证码计数
String REDIS_SMS_CODE_COUNT = "SMS_Phone_verification_code_count:"; String REDIS_SMS_CODE_COUNT = "SMS_Phone_verification_code_count:";
String REDIS_SMS_IP_COUNT = "SMS_Ip_verification_code_count:";
String REDIS_SMS_DEVICE_COUNT = "SMS_Device_verification_code_count:"; String REDIS_SMS_DEVICE_COUNT = "SMS_Device_verification_code_count:";
//新版语音验证码计数 //新版语音验证码计数
String REDIS_VOICE_CODE_COUNT = "Voice_Phone_verification_code_count:"; String REDIS_VOICE_CODE_COUNT = "Voice_Phone_verification_code_count:";
String REDIS_VOICE_IP_COUNT = "Voice_Ip_verification_code_count:";
String REDIS_VOICE_DEVICE_COUNT = "Voice_Device_verification_code_count:"; String REDIS_VOICE_DEVICE_COUNT = "Voice_Device_verification_code_count:";
String REDIS_VERIFICATION_COUNT = "verification_code_count:"; String REDIS_VERIFICATION_COUNT = "verification_code_count:";
...@@ -41,30 +26,49 @@ public interface Constants { ...@@ -41,30 +26,49 @@ public interface Constants {
String X_AUTH_TOKEN = "x-auth-token"; String X_AUTH_TOKEN = "x-auth-token";
// -- Start -- IPV4安全策略常量组 // -- Start -- IPV4安全策略常量组
// 账密不匹配错误 - 按账号计数
String REDIS_PASSWORD_ERROR_COUNT = "password_error_count:"; String REDIS_PASSWORD_ERROR_COUNT = "password_error_count:";
// 账密不匹配错误 - 按IP计数
String REDIS_PASSWORD_ERROR_COUNT_FOR_IPV4 = "password_error_count_4_ipv4:"; String REDIS_PASSWORD_ERROR_COUNT_FOR_IPV4 = "password_error_count_4_ipv4:";
// 账密匹配成功 - 按IP计数
String REDIS_PASSWORD_SUCCESS_COUNT_FOR_IPV4 = "password_success_count_4_ipv4:";
// 账密安全策略 - 白名单
String IPV4_LOCK_WHITE = "lock_ipv4:white:"; String IPV4_LOCK_WHITE = "lock_ipv4:white:";
// 账密安全策略 - 黑名单
String IPV4_LOCK_BLACK = "lock_ipv4:black:"; String IPV4_LOCK_BLACK = "lock_ipv4:black:";
// 账密安全策略 - 锁机制自定义参数 - 锁定分钟数
String IPV4_LOCK_MINUTES_REDIS = "lock_ipv4:minutes:"; String IPV4_LOCK_MINUTES_REDIS = "lock_ipv4:minutes:";
// 账密安全策略 - 锁机制自定义参数 - 锁开关阈值
String IPV4_LOCK_ON_COUNTS_REDIS = "lock_ipv4:on_counts:"; String IPV4_LOCK_ON_COUNTS_REDIS = "lock_ipv4:on_counts:";
// 账密安全策略 - 锁机制 - IPV4锁
String IPV4_LOCK = "lock_ipv4:"; String IPV4_LOCK = "lock_ipv4:";
Long IPV4_LOCK_MINUTES = 3 * 60L; // 账密不匹配错误 - 锁机制默认参数 - 锁定分钟数
Long IPV4_COUNT_MINUTES = 1L; Long IPV4_FAILED_LOCK_MINUTES = 3 * 60L;
Long IPV4_LOCK_ON_COUNTS = 60L; // 账密不匹配错误 - 锁机制默认参数 - 计数周期
Long IPV4_FAILED_COUNT_MINUTES = 1L;
// 账密不匹配错误 - 锁机制默认参数 - 锁开关阈值
Long IPV4_LOCK_ON_FAILED_COUNTS = 60L;
// 账密匹配成功 - 锁机制默认参数 - 锁定分钟数
Long IPV4_SUCCESS_LOCK_MINUTES = 30L;
// 账密匹配成功 - 锁机制默认参数 - 计数周期
Long IPV4_SUCCESS_COUNT_MINUTES = 1 * 60L;
// 账密匹配成功 - 锁机制默认参数 - 锁开关阈值
Long IPV4_LOCK_ON_SUCCESS_COUNTS = 40L;
// 危险期 - 起始时间(Hour)
int DANGEROUS_TIME_START = 22; int DANGEROUS_TIME_START = 22;
// 危险期 - 结束时间(Hour)
int DANGEROUS_TIME_END = 6; int DANGEROUS_TIME_END = 6;
// 安全策略参数设置 - 秘钥 - 口令
String CLEAR_LOCK_FOR_IPV4 = "x-clear-lock-11241842-y"; String CLEAR_LOCK_FOR_IPV4 = "x-clear-lock-11241842-y";
// 安全策略参数设置 - 私钥
String CLEAR_LOCK_FOR_IPV4_KEY = "lhp.family.dwy.sjs.yym.cxy.cpg"; String CLEAR_LOCK_FOR_IPV4_KEY = "lhp.family.dwy.sjs.yym.cxy.cpg";
// -- End -- IPV4安全策略常量组 // -- End -- IPV4安全策略常量组
/** /**
* redis中token的key值前缀 * redis中token的key值前缀
*/ */
String SESSION_PREFIX = "spring:session:sessions:"; String SESSION_PREFIX = "spring:session:sessions:";
Long ONE_DAY = 24 * 60 * 60L;
interface Channel { interface Channel {
long LKB = 1; // 量化派
long JR58 = 175; // 58金融
long BAITIAO = 222L; long BAITIAO = 222L;
String LKB_CODE = "0002"; // 量化派channnel_code String LKB_CODE = "0002"; // 量化派channnel_code
long WECHAT = 198L; long WECHAT = 198L;
...@@ -77,18 +81,8 @@ public interface Constants { ...@@ -77,18 +81,8 @@ public interface Constants {
Long ONE_DAY = 24 * 60 * 60L; Long ONE_DAY = 24 * 60 * 60L;
} }
interface WeChat {
String APP_ID = "wx0a7c0bce0c3ac523";
String REDIRECT_URL = "http://wechattest.xyqb.com/webchat/receiveCode";
String SCOPE = "snsapi_userinfo";
}
interface UserAvatar { interface UserAvatar {
String AVATAR_DEFAULT = "https://avatar.xyqb.com/default_avatar.png"; String AVATAR_DEFAULT = "https://avatar.xyqb.com/default_avatar.png";
} }
interface Sms {
String VERIFICATION_CODE = "尊敬的用户,您本次的验证码为:%s,有效期10分钟。"; // 随机验证码
String BINDCARD_SMS = "用户您好,您已绑卡成功,将会在1-5个工作日内收到借款,请耐心等待。如有疑问,请致电400-002-0061,感谢您对我们的支持";//绑卡成功后的短信文案
String REPAY_SMS = "用户您好,您在信用钱包的本期账单已还款成功,保持良好的信用可升级为VIP用户,享更多特权,感谢您对信用钱包的支持";
}
} }
...@@ -76,12 +76,6 @@ public class LockIpv4Controller implements IBaseController { ...@@ -76,12 +76,6 @@ public class LockIpv4Controller implements IBaseController {
// 操作标记 // 操作标记
boolean lock = Objects.equals(Boolean.TRUE.toString(), act); boolean lock = Objects.equals(Boolean.TRUE.toString(), act);
boolean valid = ValidationUtil.isValid(key, lock); boolean valid = ValidationUtil.isValid(key, lock);
// Todo - 兼容简单验证规则,暂时保留
if(!valid){
Calendar now = Calendar.getInstance();
int hour = now.get(Calendar.HOUR_OF_DAY);
valid = Objects.equals(Constants.CLEAR_LOCK_FOR_IPV4_KEY+hour, key);
}
if(valid){ if(valid){
lockIpv4(ip, lock); lockIpv4(ip, lock);
return JsonResult.buildSuccessResult("Success",null); return JsonResult.buildSuccessResult("Success",null);
...@@ -101,19 +95,19 @@ public class LockIpv4Controller implements IBaseController { ...@@ -101,19 +95,19 @@ public class LockIpv4Controller implements IBaseController {
String lockIpv4Key = getLockIpv4Key(ip); String lockIpv4Key = getLockIpv4Key(ip);
if(lock){ if(lock){
// 每分钟计数阈值 // 每分钟计数阈值
long counts = Constants.IPV4_LOCK_ON_COUNTS; long counts = Constants.IPV4_LOCK_ON_FAILED_COUNTS;
String redisCounts = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS); String redisCounts = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
if(StringUtils.isNumeric(redisCounts) && Integer.valueOf(redisCounts) > 0){ if(StringUtils.isNumeric(redisCounts) && Integer.valueOf(redisCounts) > 0){
counts = Integer.valueOf(redisCounts); counts = Integer.valueOf(redisCounts);
} }
// 锁定时长 // 锁定时长
long minutes = Constants.IPV4_LOCK_MINUTES; long minutes = Constants.IPV4_FAILED_LOCK_MINUTES;
String redisMinutes = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS); String redisMinutes = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
if(StringUtils.isNumeric(redisMinutes) && Integer.valueOf(redisMinutes) > 0){ if(StringUtils.isNumeric(redisMinutes) && Integer.valueOf(redisMinutes) > 0){
minutes = Integer.valueOf(redisMinutes); minutes = Integer.valueOf(redisMinutes);
} }
redisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), minutes, TimeUnit.MINUTES); redisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), minutes, TimeUnit.MINUTES);
LOGGER.info("Lock_ipv4: locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, counts, Constants.IPV4_COUNT_MINUTES, minutes); LOGGER.info("Lock_ipv4: locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, counts, Constants.IPV4_FAILED_COUNT_MINUTES, minutes);
}else{ }else{
redisTemplate.delete(lockIpv4Key); redisTemplate.delete(lockIpv4Key);
LOGGER.info("Lock_ipv4: unlocked ip Success. ip:{}", ip); LOGGER.info("Lock_ipv4: unlocked ip Success. ip:{}", ip);
...@@ -239,7 +233,7 @@ public class LockIpv4Controller implements IBaseController { ...@@ -239,7 +233,7 @@ public class LockIpv4Controller implements IBaseController {
}else{ }else{
redisTemplate.delete(Constants.IPV4_LOCK_ON_COUNTS_REDIS); redisTemplate.delete(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
redisTemplate.delete(Constants.IPV4_LOCK_MINUTES_REDIS); redisTemplate.delete(Constants.IPV4_LOCK_MINUTES_REDIS);
LOGGER.info("Lock_ipv4: remove redis-param counts、minutes Success, counts:{},minutes:{}, current default:[counts:{},minutes:{}]", Constants.IPV4_LOCK_ON_COUNTS, Constants.IPV4_LOCK_MINUTES); LOGGER.info("Lock_ipv4: remove redis-param counts、minutes Success, counts:{},minutes:{}, current default:[counts:{},minutes:{}]", Constants.IPV4_LOCK_ON_FAILED_COUNTS, Constants.IPV4_FAILED_LOCK_MINUTES);
} }
} }
......
...@@ -119,8 +119,8 @@ public class UserController implements IBaseController { ...@@ -119,8 +119,8 @@ public class UserController implements IBaseController {
if(valid){ if(valid){
String lockIpv4Key = getLockIpv4Key(ip); String lockIpv4Key = getLockIpv4Key(ip);
if(lock){ if(lock){
stringRedisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), Constants.IPV4_LOCK_MINUTES, TimeUnit.MINUTES); stringRedisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), Constants.IPV4_FAILED_LOCK_MINUTES, TimeUnit.MINUTES);
LOGGER.info("Locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, Constants.IPV4_LOCK_ON_COUNTS, Constants.IPV4_COUNT_MINUTES, Constants.IPV4_LOCK_MINUTES); LOGGER.info("Locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, Constants.IPV4_LOCK_ON_FAILED_COUNTS, Constants.IPV4_FAILED_COUNT_MINUTES, Constants.IPV4_FAILED_LOCK_MINUTES);
}else{ }else{
stringRedisTemplate.delete(lockIpv4Key); stringRedisTemplate.delete(lockIpv4Key);
LOGGER.info("Clear_or_lock ip Success:{}", ip); LOGGER.info("Clear_or_lock ip Success:{}", ip);
...@@ -553,7 +553,7 @@ public class UserController implements IBaseController { ...@@ -553,7 +553,7 @@ public class UserController implements IBaseController {
countErrorByIpv4(); countErrorByIpv4();
return null; return null;
} }
LOGGER.info("用户正在登录... [{}]", credentialArr); LOGGER.info("用户正在登录... [{}]", bufStr);
String phoneNo = credentialArr[0]; String phoneNo = credentialArr[0];
String pass = credentialArr[1]; String pass = credentialArr[1];
User user = userService.findByPhoneWithCache(phoneNo); User user = userService.findByPhoneWithCache(phoneNo);
...@@ -572,6 +572,8 @@ public class UserController implements IBaseController { ...@@ -572,6 +572,8 @@ public class UserController implements IBaseController {
countErrorByIpv4(); countErrorByIpv4();
return null; return null;
} }
// 向该ipv4添加成功计数器
countSuccessByIpv4();
return user; return user;
} }
...@@ -589,8 +591,10 @@ public class UserController implements IBaseController { ...@@ -589,8 +591,10 @@ public class UserController implements IBaseController {
// 密码错误计数 // 密码错误计数
Long errorCount = stringRedisTemplate.opsForValue().increment(key, 1L); Long errorCount = stringRedisTemplate.opsForValue().increment(key, 1L);
if (errorCount > Constants.Image_Need_Count) { if (errorCount > Constants.Image_Need_Count) {
LOGGER.info("用户名或密码不正确,phoneNo={}", phoneNo);
throw new PasswordErrorLimitException("用户名或密码不正确"); throw new PasswordErrorLimitException("用户名或密码不正确");
} else if (Objects.equals(errorCount, Constants.Image_Need_Count)) { } else if (Objects.equals(errorCount, Constants.Image_Need_Count)) {
LOGGER.info("请输入图形验证码,phoneNo={}", phoneNo);
throw new PasswordErrorLimitException("请输入图形验证码"); throw new PasswordErrorLimitException("请输入图形验证码");
} }
} }
...@@ -605,14 +609,14 @@ public class UserController implements IBaseController { ...@@ -605,14 +609,14 @@ public class UserController implements IBaseController {
}*/ }*/
String ipv4 = getIp(); String ipv4 = getIp();
if (StringUtils.isNotBlank(ipv4) && !ValidationUtil.validateLocalIpv4(ipv4)) { if (StringUtils.isNotBlank(ipv4) && !ValidationUtil.validateLocalIpv4(ipv4)) {
String ipv4Key = getIpKey(ipv4); String ipv4Key = getErrorIpKey(ipv4);
if(!stringRedisTemplate.hasKey(ipv4Key)){ if(!stringRedisTemplate.hasKey(ipv4Key)){
// 计数周期1分钟 // 计数周期1分钟
stringRedisTemplate.opsForValue().set(ipv4Key, String.valueOf(0), Constants.IPV4_COUNT_MINUTES, TimeUnit.MINUTES); stringRedisTemplate.opsForValue().set(ipv4Key, String.valueOf(0), Constants.IPV4_FAILED_COUNT_MINUTES, TimeUnit.MINUTES);
} }
Long count = stringRedisTemplate.opsForValue().increment(ipv4Key, 1L); Long count = stringRedisTemplate.opsForValue().increment(ipv4Key, 1L);
LOGGER.info("Lock_ipv4: count ip access: ip={}, count={}", ipv4, count); LOGGER.info("Lock_ipv4: count ip access: ip={}, count={}", ipv4, count);
lockIpv4(ipv4, count); lockErrorIpv4(ipv4, count);
} }
} }
...@@ -621,9 +625,9 @@ public class UserController implements IBaseController { ...@@ -621,9 +625,9 @@ public class UserController implements IBaseController {
* @param ip - 目标ip * @param ip - 目标ip
* @param count - 错误计数 * @param count - 错误计数
*/ */
private void lockIpv4(String ip, long count){ private void lockErrorIpv4(String ip, long count){
// 每分钟计数阈值 // 每分钟计数阈值
long counts = Constants.IPV4_LOCK_ON_COUNTS; long counts = Constants.IPV4_LOCK_ON_FAILED_COUNTS;
String redisCounts = stringRedisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS); String redisCounts = stringRedisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
if(StringUtils.isNumeric(redisCounts) && Integer.valueOf(redisCounts) > 0){ if(StringUtils.isNumeric(redisCounts) && Integer.valueOf(redisCounts) > 0){
counts = Integer.valueOf(redisCounts); counts = Integer.valueOf(redisCounts);
...@@ -632,20 +636,61 @@ public class UserController implements IBaseController { ...@@ -632,20 +636,61 @@ public class UserController implements IBaseController {
return; return;
} }
// 锁定时长 // 锁定时长
long minutes = Constants.IPV4_LOCK_MINUTES; long minutes = Constants.IPV4_FAILED_LOCK_MINUTES;
String redisMinutes = stringRedisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS); String redisMinutes = stringRedisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
if(StringUtils.isNumeric(redisMinutes) && Integer.valueOf(redisMinutes) > 0){ if(StringUtils.isNumeric(redisMinutes) && Integer.valueOf(redisMinutes) > 0){
minutes = Integer.valueOf(redisMinutes); minutes = Integer.valueOf(redisMinutes);
} }
String lockIpv4Key = getLockIpv4Key(ip); String lockIpv4Key = getLockIpv4Key(ip);
stringRedisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), minutes, TimeUnit.MINUTES); stringRedisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), minutes, TimeUnit.MINUTES);
LOGGER.info("Lock_ipv4: locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, counts, Constants.IPV4_COUNT_MINUTES, minutes); LOGGER.info("Lock_ipv4: locked ip access:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, counts, Constants.IPV4_FAILED_COUNT_MINUTES, minutes);
} }
private final static String getIpKey(String ipv4){ /**
* 向该phoneNo添加错误计数器
*/
private void countSuccessByIpv4() {
// Todo -- 全天候开放监控
/*if(!ValidationUtil.isAtDangerousTime()){
return;
}*/
String ipv4 = getIp();
if (StringUtils.isNotBlank(ipv4) && !ValidationUtil.validateLocalIpv4(ipv4)) {
String ipv4Key = getSuccessIpKey(ipv4);
if(!stringRedisTemplate.hasKey(ipv4Key)){
// 计数周期1分钟
stringRedisTemplate.opsForValue().set(ipv4Key, String.valueOf(0), Constants.IPV4_SUCCESS_COUNT_MINUTES, TimeUnit.MINUTES);
}
Long count = stringRedisTemplate.opsForValue().increment(ipv4Key, 1L);
LOGGER.info("Lock_ipv4: count ip access: ip={}, count={}", ipv4, count);
lockSuccessIpv4(ipv4, count);
}
}
/**
* 锁定IPV4
* @param ip - 目标ip
* @param count - 错误计数
*/
private void lockSuccessIpv4(String ip, long count){
// 每小时计数阈值
if(count < Constants.IPV4_LOCK_ON_SUCCESS_COUNTS){
return;
}
// 锁定时长
String lockIpv4Key = getLockIpv4Key(ip);
stringRedisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), Constants.IPV4_SUCCESS_LOCK_MINUTES, TimeUnit.MINUTES);
LOGGER.info("Lock_ipv4: locked ip access:{}, success overstep {} times in {} minutes, do lock {} minutes", ip, Constants.IPV4_LOCK_ON_SUCCESS_COUNTS, Constants.IPV4_SUCCESS_COUNT_MINUTES, Constants.IPV4_SUCCESS_LOCK_MINUTES);
}
private final static String getErrorIpKey(String ipv4){
return Constants.REDIS_PASSWORD_ERROR_COUNT_FOR_IPV4 + ipv4; return Constants.REDIS_PASSWORD_ERROR_COUNT_FOR_IPV4 + ipv4;
} }
private final static String getSuccessIpKey(String ipv4){
return Constants.REDIS_PASSWORD_SUCCESS_COUNT_FOR_IPV4 + ipv4;
}
private final static String getLockIpv4Key(String ipv4){ private final static String getLockIpv4Key(String ipv4){
return Constants.IPV4_LOCK + ipv4; return Constants.IPV4_LOCK + ipv4;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment