場景:
發送短信屬於付費業務,有時爲了防止短信攻擊,須要限制發送短信的頻率,例如在1個小時以內最多發送11條短信.
如何實現呢?java
Date now=new Date(); Date oneHourAgo= //1個小時以前的時刻 //查詢條件有兩個:時間範圍,手機號 List<SMS> smsList=this.smsService.query(fromTime,toTime,mobile); if(smsList.size()>11){ System.out.println("超出限制,禁止發送"); }else{ System.out.println("能夠發送"); }
dao中:redis
/*** * 獲取指定時間長度(範圍)內,發送的短信次數 <br /> * 在指定時間長度(範圍)內,發送的短信次數是否超出限制 * @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; }
每次發送短信,要寫入當前時間戳到redis:
String mobile="13718486139"; String time=String.valueOf(DateTimeUtil.getCurrentMillisecond()); RedisHelper.getInstance().saveKeyCache("limit_one_hour", mobile+"_"+time, time);
this
檢查時先獲取全部時間戳:.net
Map map=RedisHelper.getInstance().getAllKeyCache("limit_one_hour");
具體判斷邏輯:code
@Test public void test_limitOneHour2(){ String mobile="13718486139"; int limitCount=11; int limitTime=60*60;//1小時,單位:秒 Map<String,String> map=new HashMap<String,String>(); map.put("13718486139_1445429819328", "1445431479437"); map.put("13718486139_1445429874699", "1445431485996"); map.put("13718486139_1445429874799", "1445431491527"); map.put("13718486139_1445430757886", "1445431496853"); System.out.println(map); List<Long>list=new ArrayList<Long>(); for(String key:map.keySet()){ if(key.startsWith(mobile)){ list.add(Long.parseLong(map.get(key))/1000); } } SortList<Long>sortUtil=new SortList<Long>(); sortUtil.Sort(list, "longValue", "desc"); int length=list.size(); int toIndex=0;//要截取的最大序號 if(limitCount>length){ toIndex=length; }else{ toIndex=limitCount; } List<Long>result=list.subList(0, toIndex); long delter=list.get(0).longValue()-list.get(toIndex-1).longValue(); long delterSecond=delter; System.out.println(delterSecond); if(delterSecond<limitTime){ System.out.println("超限"); }else{ System.out.println("能夠繼續發短信"); } System.out.println(result); }
步驟:
(1)把當前手機號的全部時間戳放入list中;orm
(2)對list排序,按時間順序,從大到小;(時間越大,表示離如今越近)blog
(3)根據次數(limitCount)限制 來截取list;排序
(4)計算list中第一個元素和最後一個元素的差量,即limitCount條短信的時間跨度delterget
(5)若delter 小於時間限制limitTime,則表示超過限制,那麼禁止發送短信
優化以後的代碼:
public static boolean isLimit() { long n = System.currentTimeMillis(); Map records = RedisCacheUtil2.getPushRecordList(); if (ValueWidget.isNullOrEmpty(records)) { return false; } List<String> timestamps = new ArrayList<String>(records.values()); SortList<String> sortUtil = new SortList<String>(); 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<String> 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; } }
參考: https://my.oschina.net/hanchao/blog/1833612
參考:http://hw1287789687.iteye.com/blog/2250898