基于Java和Lua實(shí)現(xiàn)IP鎖定功能
Lua
是一種輕量級(jí)的腳本語(yǔ)言,設(shè)計(jì)之初是為了嵌入應(yīng)用程序中。它具有簡(jiǎn)單易懂的語(yǔ)法和較高的執(zhí)行效率,因此常用于游戲開(kāi)發(fā)、配置腳本、數(shù)據(jù)處理等領(lǐng)域。Lua
腳本通常被集成到其他程序中,實(shí)現(xiàn)特定功能或邏輯。
Lua 簡(jiǎn)介
Lua 是一個(gè)小巧的腳本語(yǔ)言,是巴西里約熱內(nèi)盧天主教大學(xué)里的一個(gè)研究小組于1993年開(kāi)發(fā)的。
Lua 使用標(biāo)準(zhǔn) C 語(yǔ)言編寫并以源代碼形式開(kāi)放,幾乎在所有操作系統(tǒng)和平臺(tái)上都能編譯運(yùn)行。Lua 腳本可以調(diào)用 C/C++ 的函數(shù),也可以被 C/C++ 代碼調(diào)用,所以 Lua 在應(yīng)用程序中可以被廣泛應(yīng)用。
Lua 并沒(méi)有提供強(qiáng)大的庫(kù),這是由它的定位決定的。所以 Lua 不適合作為開(kāi)發(fā)獨(dú)立應(yīng)用程序的語(yǔ)言。其設(shè)計(jì)目的是為了通過(guò)靈活嵌入應(yīng)用程序中從而為應(yīng)用程序提供靈活的擴(kuò)展和定制功能。
Lua 體積小、啟動(dòng)速度快,一個(gè)完整的 Lua 解釋器不過(guò)200k,在所有腳本引擎中,Lua 的速可以于說(shuō)是最快的。所以 Lua 是作為嵌入式腳本的最佳選擇。這也就是我們?yōu)槭裁匆獙W(xué)習(xí) Lua 這門語(yǔ)言。
那 Lua 語(yǔ)言能干嗎呢?其實(shí)它主要是用作腳本語(yǔ)言,用來(lái)開(kāi)發(fā)腳本,例如編寫游戲輔助腳本,在 Redis 中使用 Lua 腳本等。
Lua 官網(wǎng)地址:www.lua.org/
Lua 特性
輕量級(jí):Lua 使用標(biāo)準(zhǔn) C 語(yǔ)言編寫,Lua 語(yǔ)言的官方版本只包括一個(gè)精簡(jiǎn)的核心和最基本的庫(kù),體積小、啟動(dòng)速度快,一個(gè)完整的 Lua 解釋器不過(guò)200k,適合嵌入在別的程序里。
可擴(kuò)展:Lua 提供了非常易于使用的擴(kuò)展接口和機(jī)制,由宿主語(yǔ)言(通常是 C 或 C++ )提供這些功能,Lua 可以使用它們,就像是本來(lái)就內(nèi)置的功能一樣。
其它特性:
- 支持面向過(guò)程( procedure-oriented )編程和函數(shù)式編程( functional programming );
- 自動(dòng)內(nèi)存管理;
- 只提供了一種通用類型的表(table),但可以用它實(shí)現(xiàn)數(shù)組,哈希表,集合,對(duì)象;
- 閉包( closure ),通過(guò)閉包和表可以很方便地支持面向?qū)ο缶幊趟枰囊恍╆P(guān)鍵機(jī)制,比如數(shù)據(jù)抽象,虛函數(shù),繼承和重載等。;
- 提供多線程(協(xié)同進(jìn)程,并非操作系統(tǒng)所支持的線程)支持;
應(yīng)用場(chǎng)景
- 游戲開(kāi)發(fā),例如游戲輔助腳本。
- 應(yīng)用腳本,例如 Redis 使用 Lua 腳本。
- 數(shù)據(jù)庫(kù)插件,例如 MySQL Proxy 和 MySQL WorkBench。
- 安全系統(tǒng),如入侵檢測(cè)系統(tǒng)。
Lua 腳本的基本語(yǔ)法
Lua
的語(yǔ)法簡(jiǎn)單直觀,以下是一些常用的語(yǔ)法:
- 變量:使用
local
關(guān)鍵字聲明局部變量,例如:local a = 10
- 條件語(yǔ)句:使用
if
、then
、elseif
、else
、end
實(shí)現(xiàn)條件判斷,例如:
if x > 0 then print("x is positive") elseif x == 0 then print("x is zero") else print("x is negative") end
- 循環(huán):支持
for
和while
循環(huán),例如:
for i = 1, 10 do print(i) end
- 函數(shù):使用
function
定義函數(shù),例如:
function add(a, b) return a + b end
使用 Redis + Lua 腳本實(shí)現(xiàn)限制 IP 多次輸入錯(cuò)誤密碼功能
Redis
是一個(gè)高性能的鍵值數(shù)據(jù)庫(kù),支持 Lua
腳本以實(shí)現(xiàn)原子操作。以下是通過(guò) Redis
和 Lua
腳本限制同一 IP
多次輸入錯(cuò)誤密碼的實(shí)現(xiàn)。
應(yīng)用場(chǎng)景
假設(shè)我們要限制同一 IP
在短時(shí)間內(nèi)(例如 10 分鐘)輸入錯(cuò)誤密碼不超過(guò) 5 次,否則將鎖定該 IP
一段時(shí)間。
Lua 腳本示例
-- 限制IP多次輸入錯(cuò)誤密碼的Lua腳本 local ip = KEYS[1] local current_time = tonumber(ARGV[1]) local expire_time = 600 -- 10分鐘 local max_attempts = 5 local lock_duration = 3600 -- 鎖定時(shí)間1小時(shí) -- 獲取當(dāng)前錯(cuò)誤計(jì)數(shù)和上次錯(cuò)誤時(shí)間 local attempts = tonumber(redis.call('get', ip) or '0') local lock_time = tonumber(redis.call('get', ip .. ':lock') or '0') -- 檢查是否已被鎖定 if current_time < lock_time then return -1 -- -1表示IP已被鎖定 end -- 更新錯(cuò)誤計(jì)數(shù) attempts = attempts + 1 if attempts >= max_attempts then -- 超過(guò)最大嘗試次數(shù),進(jìn)行鎖定 redis.call('set', ip .. ':lock', current_time + lock_duration) return -1 else -- 未達(dá)到最大次數(shù),更新錯(cuò)誤計(jì)數(shù) redis.call('set', ip, attempts) redis.call('expire', ip, expire_time) return attempts end
當(dāng)然,以下是上述 Lua
腳本中每個(gè)參數(shù)和變量的解釋:
KEYS[1]
(ip):- 這是傳遞給 Lua 腳本的第一個(gè)鍵參數(shù),表示需要限制的 IP 地址。通過(guò)這個(gè)參數(shù),Redis 可以針對(duì)特定 IP 進(jìn)行操作。
ARGV[1]
(current_time):- 這是傳遞給 Lua 腳本的第一個(gè)參數(shù),表示當(dāng)前的 Unix 時(shí)間戳(以秒為單位)。這個(gè)時(shí)間戳用于檢查 IP 是否已經(jīng)被鎖定以及更新鎖定時(shí)間。
expire_time
:- 表示錯(cuò)誤嘗試計(jì)數(shù)的有效期,這里設(shè)定為 600 秒(10 分鐘)。在此時(shí)間內(nèi),如果錯(cuò)誤嘗試次數(shù)沒(méi)有達(dá)到最大限制,計(jì)數(shù)會(huì)自動(dòng)失效。
max_attempts
:- 最大允許的錯(cuò)誤嘗試次數(shù)。這里設(shè)置為 5 次,意味著在 10 分鐘內(nèi),如果某個(gè) IP 輸入錯(cuò)誤密碼的次數(shù)達(dá)到 5 次,則會(huì)觸發(fā)鎖定機(jī)制。
lock_duration
:- 鎖定時(shí)長(zhǎng),單位為秒。這里設(shè)置為 3600 秒(1 小時(shí))。如果 IP 被鎖定,它將在 1 小時(shí)內(nèi)無(wú)法進(jìn)行任何新的嘗試。
attempts
:- 當(dāng)前錯(cuò)誤嘗試計(jì)數(shù),從 Redis 中獲取。使用
redis.call('get', ip)
來(lái)獲取指定 IP 的錯(cuò)誤次數(shù),如果不存在則默認(rèn)為 0。
- 當(dāng)前錯(cuò)誤嘗試計(jì)數(shù),從 Redis 中獲取。使用
lock_time
:- IP 的鎖定截止時(shí)間,從 Redis 中獲取。使用
redis.call('get', ip .. ':lock')
獲取當(dāng)前 IP 的鎖定時(shí)間戳,如果不存在則默認(rèn)為 0。
- IP 的鎖定截止時(shí)間,從 Redis 中獲取。使用
腳本邏輯流程
- 首先,從
Redis
中獲取當(dāng)前IP
的錯(cuò)誤嘗試次數(shù)和鎖定時(shí)間。 - 檢查當(dāng)前時(shí)間是否小于鎖定時(shí)間,若是,說(shuō)明
IP
已被鎖定,返回-1
。 - 如果未鎖定,增加錯(cuò)誤嘗試次數(shù)。
- 檢查嘗試次數(shù)是否達(dá)到或超過(guò)最大次數(shù) (
max_attempts
)。- 如果達(dá)到或超過(guò),則鎖定
IP
,設(shè)置鎖定時(shí)間,并返回-1
。 - 如果未達(dá)到,則更新嘗試次數(shù),并設(shè)置嘗試次數(shù)的過(guò)期時(shí)間為
expire_time
。
- 如果達(dá)到或超過(guò),則鎖定
使用 Redis 執(zhí)行 Lua 腳本
可以使用 Redis 提供的 EVAL
命令執(zhí)行上述 Lua 腳本。以下是一個(gè)示例:
redis-cli --eval limit_login_attempts.lua 192.168.1.1 , 1620000000
在這里,192.168.1.1
是 IP 地址,1620000000
是當(dāng)前的 Unix 時(shí)間戳。
通過(guò)這種方式,可以有效限制一個(gè) IP 在短時(shí)間內(nèi)多次輸入錯(cuò)誤密碼,防止暴力 破解攻擊。這種機(jī)制可以集成到登錄系統(tǒng)中,以增強(qiáng)安全性。
為了更具體地結(jié)合項(xiàng)目應(yīng)用場(chǎng)景,我們可以考慮一個(gè)示例場(chǎng)景:在一個(gè)用戶登錄系統(tǒng)中,需要限制某個(gè) IP
地址在短時(shí)間內(nèi)多次輸入錯(cuò)誤密碼以防止暴力攻擊。以下是一個(gè)完整的 Java
應(yīng)用示例,演示如何使用 Redis
和 Lua
腳本來(lái)實(shí)現(xiàn)這一功能。
項(xiàng)目背景
在用戶登錄系統(tǒng)中,我們希望限制某個(gè) IP
地址在 10 分鐘內(nèi)最多輸入錯(cuò)誤密碼 5 次。如果超過(guò)這個(gè)次數(shù),則在接下來(lái)的 1 小時(shí)內(nèi)禁止該 IP 的登錄嘗試。
Java 代碼示例
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class LoginAttemptLimiter { private static final String LUA_SCRIPT = "local ip = KEYS[1] " + "local current_time = tonumber(ARGV[1]) " + "local expire_time = 600 " + // 10 minutes in seconds "local max_attempts = 5 " + "local lock_duration = 3600 " + // 1 hour in seconds "local attempts = tonumber(redis.call('get', ip) or '0') " + "local lock_time = tonumber(redis.call('get', ip .. ':lock') or '0') " + "if current_time < lock_time then " + " return -1 " + // IP is locked "end " + "attempts = attempts + 1 " + "if attempts >= max_attempts then " + " redis.call('set', ip .. ':lock', current_time + lock_duration) " + " return -1 " + // Lock the IP "else " + " redis.call('set', ip, attempts) " + " redis.call('expire', ip, expire_time) " + " return attempts " + // Return current attempt count "end"; private final JedisPool jedisPool; public LoginAttemptLimiter(JedisPool jedisPool) { this.jedisPool = jedisPool; } public int checkLoginAttempts(String ip) { try (Jedis jedis = jedisPool.getResource()) { long currentTime = System.currentTimeMillis() / 1000; // Get current time in seconds Object result = jedis.eval(LUA_SCRIPT, 1, ip, String.valueOf(currentTime)); return ((Long) result).intValue(); } } public static void main(String[] args) { // Create a connection pool to the Redis server try (JedisPool jedisPool = new JedisPool("localhost", 6379)) { LoginAttemptLimiter limiter = new LoginAttemptLimiter(jedisPool); String ip = "192.168.1.1"; int attemptResult = limiter.checkLoginAttempts(ip); if (attemptResult == -1) { System.out.println("IP is locked due to too many failed attempts."); } else { System.out.println("Failed attempt count for IP: " + attemptResult); } } } }
代碼解釋
- Lua Script: 定義了一個(gè)
Lua
腳本,它檢查和更新錯(cuò)誤登錄嘗試次數(shù),并在超過(guò)限制時(shí)鎖定IP
。 - JedisPool: 用于管理
Redis
連接,保證多線程環(huán)境下的連接安全。 - checkLoginAttempts 方法: 執(zhí)行
Lua
腳本并返回結(jié)果,表示當(dāng)前錯(cuò)誤嘗試次數(shù)或是否被鎖定。 - Main 方法: 示例中使用特定 IP 地址進(jìn)行測(cè)試,執(zhí)行登錄嘗試檢查并輸出結(jié)果。
在上述 Java
代碼中,eval
方法用于執(zhí)行 Lua
腳本,并傳遞參數(shù)給腳本。eval
方法的參數(shù)列表如下:
Object result = jedis.eval(LUA_SCRIPT, 1, ip, String.valueOf(currentTime));
傳參細(xì)節(jié)
LUA_SCRIPT
:- 這是要執(zhí)行的
Lua
腳本的字符串。腳本中定義了邏輯,用于限制 IP 的登錄嘗試次數(shù)。
- 這是要執(zhí)行的
1
:- 這是
eval
方法的第二個(gè)參數(shù),表示有多少個(gè)鍵(keys)被傳遞給Lua
腳本。在這個(gè)例子中,我們只傳遞了一個(gè)鍵(IP 地址),所以值是1
。
- 這是
ip
:- 這是傳遞給
Lua
腳本的鍵參數(shù)(KEYS[1]
)。在 Lua 腳本中通過(guò)KEYS[1]
來(lái)訪問(wèn)這個(gè)值。
- 這是傳遞給
String.valueOf(currentTime)
:- 這是傳遞給
Lua
腳本的附加參數(shù)(ARGV[1]
)。在 Lua 腳本中通過(guò)ARGV[1]
來(lái)訪問(wèn)這個(gè)值。這里表示當(dāng)前的Unix
時(shí)間戳,以秒為單位。
- 這是傳遞給
Lua 腳本中的參數(shù)使用
KEYS[1]
對(duì)應(yīng)傳入的ip
,即需要限制的 IP 地址。ARGV[1]
對(duì)應(yīng)傳入的currentTime
,即當(dāng)前時(shí)間,用于判斷是否需要鎖定 IP。
此結(jié)構(gòu)允許對(duì)指定的 IP 地址進(jìn)行操作,判斷其登錄嘗試情況,并根據(jù)當(dāng)前時(shí)間做出相應(yīng)處理。通過(guò) eval
方法傳遞的參數(shù),可以動(dòng)態(tài)地影響腳本的執(zhí)行邏輯。
應(yīng)用場(chǎng)景
這段代碼可以集成到實(shí)際的登錄驗(yàn)證邏輯中,例如在用戶每次輸入密碼時(shí)調(diào)用 checkLoginAttempts
方法,判斷是否允許繼續(xù)登錄嘗試。通過(guò)這種方式,可以有效地防止暴力 破解攻擊,保護(hù)用戶賬戶安全。
以上就是基于Java和Lua實(shí)現(xiàn)IP鎖定功能的詳細(xì)內(nèi)容,更多關(guān)于Java Lua實(shí)現(xiàn)IP鎖定的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot3集成ElasticSearch的方法詳解
Elasticsearch是一個(gè)分布式、RESTful風(fēng)格的搜索和數(shù)據(jù)分析引擎,適用于各種數(shù)據(jù)類型,數(shù)字、文本、地理位置、結(jié)構(gòu)化數(shù)據(jù)、非結(jié)構(gòu)化數(shù)據(jù),本文給大家詳解介紹了SpringBoot3集成ElasticSearch的方法,需要的朋友可以參考下2023-08-08詳解Spring Batch 輕量級(jí)批處理框架實(shí)踐
這篇文章主要介紹了詳解Spring Batch 輕量級(jí)批處理框架實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06SpringBoot中MyBatis使用自定義TypeHandler的實(shí)現(xiàn)
本文主要介紹了SpringBoot中MyBatis使用自定義TypeHandler,當(dāng)默認(rèn)的類型映射不能滿足需求時(shí),自定義?TypeHandler?就非常有用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08Java中的instanceof關(guān)鍵字在Android中的用法實(shí)例詳解
instanceof是Java的一個(gè)二元操作符,和==,>,<是同一類東西。接下來(lái)通過(guò)本文給大家介紹Java中的instanceof關(guān)鍵字在Android中的用法,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-07-07mybatis調(diào)用mysql存儲(chǔ)過(guò)程(返回參數(shù),單結(jié)果集,多結(jié)果集)
本文主要介紹了mybatis調(diào)用mysql存儲(chǔ)過(guò)程(返回參數(shù),單結(jié)果集,多結(jié)果集),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Springboot?手動(dòng)分頁(yè)查詢分批批量插入數(shù)據(jù)的實(shí)現(xiàn)流程
這篇文章主要介紹了Springboot?手動(dòng)分頁(yè)查詢分批批量插入數(shù)據(jù)的實(shí)現(xiàn)流程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07Java多線程實(shí)現(xiàn)Runnable方式
這篇文章主要為大家詳細(xì)介紹了Java多線程如何實(shí)現(xiàn)Runnable方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03