发送短信如何限制1小时内最多发送11条短信
场景:
发送短信属于付费业务,有时为了防止短信攻击,需要限制发送短信的频率,例如在1个小时之内最多发送11条短信.
如何实现呢?
思路有两个
截至到当前时刻的1个小时之内,看是否有超过11条短信
Date now=new Date();
Date oneHourAgo= //1个小时之前的时刻
//查询条件有两个:时间范围,手机号
List smsList=this.smsService.query(fromTime,toTime,mobile);
if(smsList.size()>11){
System.out.println("超出限制,禁止发送");
}else{
System.out.println("可以发送");
}
dao中:
/***
* 获取指定时间长度(范围)内,发送的短信次数
* 在指定时间长度(范围)内,发送的短信次数是否超出限制
* @param startDate
* @param endDate
* @param mobile
* @return
*/
public Long count(String startDate, String endDate, String mobile) {
CriteriaHelper criteriaHelper = CriteriaHelper.getInstance(this);
return criteriaHelper.between("createTime",startDate,endDate)
.eq("mobile",mobile)
.count();
}
Service中:
/***
* 在指定时间长度(范围)内,发送的短信次数是否超出限制
* @param mobile
* @return
*/
public boolean validateSMSSendCountByTimeRange(String mobile) {
Date now=new Date();
//1小时前的时刻
Date oneHouseAgo = TimeHWUtil.getDateBeforeHour(now, 1);
Long count = this.sMSDao.count(TimeHWUtil.formatDateTime(oneHouseAgo), TimeHWUtil.formatDateTime(now), mobile);
if (count >= SMSUtil.LIMITCOUNT_SEND_SMS) {
String msg="超出限制";
logger.warn(msg);
smsLogger.warn(msg);
return false;
}
return true;
}
最近的11条短信 时间跨度是否小于1小时,如果小于1小时,就禁止发送
每次发送短信,要写入当前时间戳到redis:
String mobile="13718486139"; String time=String.valueOf(DateTimeUtil.getCurrentMillisecond()); RedisHelper.getInstance().saveKeyCache("limit_one_hour", mobile+"_"+time, time);
检查时先获取所有时间戳:
Map map=RedisHelper.getInstance().getAllKeyCache("limit_one_hour");
具体判断逻辑:
@Test
public void test_limitOneHour2(){
String mobile="13718486139";
int limitCount=11;
int limitTime=60*60;//1小时,单位:秒
Map map=new HashMap();
map.put("13718486139_1445429819328", "1445431479437");
map.put("13718486139_1445429874699", "1445431485996");
map.put("13718486139_1445429874799", "1445431491527");
map.put("13718486139_1445430757886", "1445431496853");
System.out.println(map);
Listlist=new ArrayList();
for(String key:map.keySet()){
if(key.startsWith(mobile)){
list.add(Long.parseLong(map.get(key))/1000);
}
}
SortListsortUtil=new SortList();
sortUtil.Sort(list, "longValue", "desc");
int length=list.size();
int toIndex=0;//要截取的最大序号
if(limitCount>length){
toIndex=length;
}else{
toIndex=limitCount;
}
Listresult=list.subList(0, toIndex);
long delter=list.get(0).longValue()-list.get(toIndex-1).longValue();
long delterSecond=delter;
System.out.println(delterSecond);
if(delterSecond
System.out.println("超限");
}else{
System.out.println("可以继续发短信");
}
System.out.println(result);
}
步骤:
(1)把当前手机号的所有时间戳放入list中;
(2)对list排序,按时间顺序,从大到小;(时间越大,表示离现在越近)
(3)根据次数(limitCount)限制 来截取list;
(4)计算list中第一个元素和最后一个元素的差量,即limitCount条短信的时间跨度delter
(5)若delter 小于时间限制limitTime,则表示超过限制,那么禁止发送短信
优化之后的代码:
public static boolean isLimit() {
long n = System.currentTimeMillis();
Map records = RedisCacheUtil2.getPushRecordList();
if (ValueWidget.isNullOrEmpty(records)) {
return false;
}
List timestamps = new ArrayList(records.values());
SortList sortUtil = new SortList();
sortUtil.sort(timestamps, null, "desc");
// 1 分钟之内不能超过 4(limitCount)
int limitCount = 4;
int limitTime = 60 * 1000;//1 分钟,单位:豪秒
int length = timestamps.size();
if (length < limitCount) {
//没有超过限制
return false;
}
int toIndex = 0;//要截取的最大序号
/*if (limitCount + 1 > length) {
toIndex = length;
} else {*/
toIndex = limitCount;
// }
List result = timestamps.subList(0, toIndex);
//和当前时间比较
System.out.println("n :" + n);
long delter = /*result.get(0))*/n - Long.parseLong(result.get(toIndex - 1));
long delterSecond = delter;
System.out.println("delter :" + delter);
System.out.println(delterSecond);
if (delterSecond < limitTime) {
System.out.println("record :" + HWJacksonUtils.getJsonP(result));
System.out.println("timestamps :" + HWJacksonUtils.getJsonP(timestamps));
System.out.println("超限");
return true;
} else {
System.out.println("可以继续发短信");
return false;
}
}
使用限流器