在互聯網的發展史上,安全老是一個繞不開話題, 你有安全盾、我有破盾矛。所謂道高一尺、魔高一丈,不過互聯網安全也正是在這種攻防中慢慢的發展起來的。redis
不過今天寫的沒有上面說的那麼高大,只是一個小小的防刷解決思路。spring
這是工做中常常遇到的、在此僅作一個記錄,以便回顧。瀏覽器
若有不嚴謹或者不完善的地方,歡迎指正 ~謝謝~緩存
場景: 在咱們給 xx 作的區塊鏈共享出行平臺中區塊鏈瀏覽器系統的登陸是以用戶的手機號+驗證碼來登陸的。安全
因是內部用戶使用、在登陸這塊也沒作特殊的安全處理,致使在測試時被咱們的測試小哥給刷爆了(在這給測試小哥點個贊)。區塊鏈
到這咱們必然的收到一個bug了,業務指望。測試
1: 同一個ip限制一分鐘最多獲取5次ui
2: 超過5次則鎖定1小時,鎖按期間獲取短信需加圖片驗證碼lua
收到這個需求、利用Redis作了簡單的限流防刷功能。code
首先分析需求,
1: 對同一IP作限制
2: 對單位時間內次數作限制
利用Redis來實現思路
1: 一個獲取短信驗證碼請求過來咱們首先的判斷此IP是否已經被鎖定(單位時間內超過了限定的訪問次數)
2: 若是未被鎖定則判斷此IP是不是首次訪問,若是是則給此IP加個生命週期以及記錄訪問次數。
3: 若是不是首次訪問,則判斷單位時間內是否符合限制要求。
完成這三不咱們須要在 redis 中定義三個KEY
msg_lock_key_{ip}
記錄次IP已被鎖定
msg_time_key_{ip}
記錄單位時間內IP
msg_counter_key_{ip}
記錄單位時間內IP訪問次數
到這就直接上一段代碼吧,
public boolean checkMsgFrequency(String remotIp) { String romteIpString = remotIp.replace(".", ""); // 1: 判斷此ip是否已經被限制 String msgLockKey = Constants.API_MSG_LOCK_KEY + romteIpString; if (jedis.exists(msgLockKey)) { // 此ip已經被鎖住 logger.info("此Ip[{}]以超過規定訪問頻率key:{}", remotIp, msgLockKey); return false; } // 2: 判斷此ip是否在規定的時間內訪問過 String msgTimeKey = Constants.API_MSG_TIME_KEY + romteIpString; String msgCounterKey = Constants.API_MSG_COUNTER_KEY + romteIpString; // key 不存在 if (!jedis.exists(msgTimeKey)) { // 加入緩存 jedis.setEx(msgTimeKey, "0", imageCode.getLimitTime()); jedis.setEx(msgCounterKey, "1", imageCode.getLimitTime()); } if (jedis.exists(msgTimeKey) && (jedis.incrBy(msgCounterKey, 1) > imageCode.getLimitCounter())) { logger.info("此Ip[{}]以超過規定訪問頻率、進行枷鎖key:{}", remotIp, msgTimeKey); jedis.setEx(msgLockKey, "0", imageCode.getLimitlockTime()); return false; } return true; }
這是一個簡單的代碼實現, 邏輯就是這個邏輯,實現方式不少種。
你可使用spring AOP + 自定義註解對邏輯進行分裝。
也可使用Redis + lua腳本對上述三步邏輯進行分裝。
這個就看我的,重要的的理解實現的思路, 實現方式千萬種總有一種是適合你的。
對稀有資源的限流防刷,通常對單位時間的訪問頻率或者次數的限制。
咱們主要是理解其解決問題的思路, 而不是記住實現代碼。
主要思路:
1: 查看針對次請求(IP 或者 uid) 對當前請求資源是否已被鎖定。
2: 若是沒有被鎖定,則給此請求加生命週期(也就是一個限制時間單位),同時記錄訪問次數。
3: 則判斷此請求是否達到鎖定條件
大體思想就是這三步。
此記錄爲加深本身記憶,同時以便溫故,若能幫到你,萬分高興。若有不對的地方歡迎留言指正 謝謝!
如您有更好的思路歡迎交流