golang實(shí)現(xiàn)ip訪問(wèn)限制及提交次數(shù)
在 Web 應(yīng)用中,通常會(huì)需要對(duì) IP 訪問(wèn)進(jìn)行限制以及控制提交次數(shù),以防止惡意攻擊(例如暴力破解、DoS攻擊、API濫用等)。為了實(shí)現(xiàn)這一功能,我們可以結(jié)合 Golang 的特性,使用中間件或者基于 Redis 這樣的緩存服務(wù)來(lái)實(shí)現(xiàn) IP 限制和提交次數(shù)的控制。
實(shí)現(xiàn)步驟
- IP 訪問(wèn)限制:對(duì)每個(gè) IP 的訪問(wèn)頻次進(jìn)行限制,比如每個(gè) IP 每分鐘只能訪問(wèn)某個(gè)接口 10 次。超過(guò)限制后,返回錯(cuò)誤信息(例如 429 Too Many Requests)。
- 提交次數(shù)限制:通過(guò)限制某個(gè)時(shí)間段內(nèi)某個(gè) IP 的提交次數(shù),防止暴力破解或者濫用接口。
- Redis(或其他存儲(chǔ)系統(tǒng))作為計(jì)數(shù)器:為了更好地實(shí)現(xiàn)這種限制,可以使用 Redis 等緩存系統(tǒng)來(lái)存儲(chǔ) IP 的訪問(wèn)記錄、提交次數(shù)等,因?yàn)?Redis 的性能和易用性使它成為理想的選擇。
核心概念
- Rate Limiting(限流):根據(jù) IP 限制某個(gè)時(shí)間段內(nèi)的訪問(wèn)次數(shù)。
- 請(qǐng)求次數(shù)計(jì)數(shù):對(duì)每個(gè) IP 進(jìn)行計(jì)數(shù),并基于計(jì)數(shù)來(lái)判斷是否超過(guò)限制。
- 時(shí)間窗口:設(shè)置一定的時(shí)間窗口(例如一分鐘或五分鐘),在這個(gè)時(shí)間段內(nèi)統(tǒng)計(jì) IP 的訪問(wèn)次數(shù)。
使用 Golang 及 Redis 實(shí)現(xiàn) IP 訪問(wèn)限制和提交次數(shù)限制
這里我們使用 Redis 來(lái)存儲(chǔ)和控制訪問(wèn)次數(shù),并結(jié)合 Go 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IP 訪問(wèn)限制中間件。
依賴庫(kù)
你可以使用 Redis 官方的 Go 客戶端 go-redis 來(lái)連接 Redis 進(jìn)行操作。先安裝這個(gè)庫(kù):
go get github.com/go-redis/redis/v8
實(shí)現(xiàn)代碼
下面的代碼演示了如何使用 Redis 來(lái)實(shí)現(xiàn) IP 訪問(wèn)限制和提交次數(shù)限制。
package main
import (
"context"
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/go-redis/redis/v8"
)
// Redis client
var rdb *redis.Client
// 初始化 Redis 客戶端
func initRedis() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 地址
Password: "", // Redis 密碼(如果有)
DB: 0, // 使用的 Redis 數(shù)據(jù)庫(kù)
})
}
// 獲取客戶端的 IP 地址
func getIP(r *http.Request) string {
// 嘗試從 X-Forwarded-For 或 X-Real-IP 獲取真實(shí) IP
ip := r.Header.Get("X-Forwarded-For")
if ip == "" {
ip = r.Header.Get("X-Real-IP")
}
if ip == "" {
ip = r.RemoteAddr
}
return ip
}
// 中間件:IP 訪問(wèn)限制
func rateLimitMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
ip := getIP(r)
key := "rate_limit:" + ip
// 獲取 Redis 中的訪問(wèn)次數(shù)
count, err := rdb.Get(ctx, key).Result()
if err == redis.Nil {
// 如果沒(méi)有記錄,設(shè)置計(jì)數(shù)為1,并設(shè)置過(guò)期時(shí)間
err := rdb.Set(ctx, key, 1, time.Minute).Err() // 1 分鐘限制
if err != nil {
http.Error(w, "Redis error", http.StatusInternalServerError)
return
}
} else if err != nil {
http.Error(w, "Redis error", http.StatusInternalServerError)
return
} else {
// 將訪問(wèn)次數(shù)轉(zhuǎn)換為整數(shù)
countInt, _ := strconv.Atoi(count)
if countInt >= 10 { // 假設(shè)限制為每分鐘最多10次
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
// 遞增計(jì)數(shù)
rdb.Incr(ctx, key)
}
next.ServeHTTP(w, r)
}
}
// 示例處理器:提交處理
func submitHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Request successful")
}
func main() {
// 初始化 Redis
initRedis()
// 創(chuàng)建 HTTP 服務(wù)器并添加中間件
http.HandleFunc("/submit", rateLimitMiddleware(submitHandler))
log.Println("Server is running on port 8080...")
http.ListenAndServe(":8080", nil)
}
代碼解析
Redis 客戶端初始化:
- 使用
redis.NewClient()初始化 Redis 客戶端。 - 通過(guò)
rdb.Set()和rdb.Get()來(lái)操作 Redis 中的計(jì)數(shù)器。
IP 獲取:
通過(guò) getIP() 函數(shù)獲取請(qǐng)求的客戶端 IP 地址。該函數(shù)嘗試從請(qǐng)求頭中的 X-Forwarded-For 或 X-Real-IP 獲取真實(shí)的 IP。如果沒(méi)有,則使用 RemoteAddr。
Rate Limiting 中間件:
rateLimitMiddleware()是核心的中間件函數(shù),負(fù)責(zé)限制每個(gè) IP 的訪問(wèn)次數(shù)。它使用 Redis 來(lái)存儲(chǔ)每個(gè) IP 的訪問(wèn)計(jì)數(shù)和限流時(shí)間窗口(這里設(shè)置為 1 分鐘)。- 當(dāng) IP 的訪問(wèn)次數(shù)超過(guò)限制時(shí),返回 HTTP 狀態(tài)碼
429 Too Many Requests。
處理請(qǐng)求:
submitHandler()是一個(gè)簡(jiǎn)單的示例處理器,處理成功的請(qǐng)求。- 訪問(wèn)
/submit時(shí),經(jīng)過(guò)中間件限制后,正常情況下返回 "Request successful"。
改進(jìn)與擴(kuò)展
- 動(dòng)態(tài)調(diào)整限流策略: 可以根據(jù)不同的用戶類型、不同的 API 路徑動(dòng)態(tài)調(diào)整限流策略。例如,VIP 用戶可能會(huì)有更高的訪問(wèn)頻次。
- IP 黑名單: 通過(guò) Redis 或其他存儲(chǔ)系統(tǒng)維護(hù)一個(gè)黑名單,遇到黑名單中的 IP 可以直接拒絕請(qǐng)求。
- 按時(shí)間窗口的限流算法: 你可以采用滑動(dòng)窗口、漏桶算法、令牌桶算法等更復(fù)雜的限流算法來(lái)實(shí)現(xiàn)更靈活的控制。
- 使用 Redis Expire 特性: 在 Redis 中使用
SetEX(帶過(guò)期時(shí)間的鍵設(shè)置)或TTL來(lái)確保計(jì)數(shù)器可以自動(dòng)重置,避免手動(dòng)管理。 - 日志記錄與報(bào)警: 可以結(jié)合日志系統(tǒng),在某個(gè) IP 頻繁觸發(fā)限制時(shí)記錄日志或發(fā)送報(bào)警信息。
通過(guò) Golang 和 Redis 的結(jié)合,可以輕松實(shí)現(xiàn) IP 訪問(wèn)限制和提交次數(shù)控制。Redis 的高性能特性使其非常適合用作限流計(jì)數(shù)器的存儲(chǔ)。在實(shí)際應(yīng)用中,可以根據(jù)需要擴(kuò)展該方案,例如使用不同的限流算法、結(jié)合 IP 黑名單等。
到此這篇關(guān)于golang實(shí)現(xiàn)ip訪問(wèn)限制及提交次數(shù)的文章就介紹到這了,更多相關(guān)go ip訪問(wèn)限制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器
這篇文章主要為大家介紹了go語(yǔ)言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
使用Go語(yǔ)言實(shí)現(xiàn)微信公眾平臺(tái)
這篇文章主要介紹了使用Go語(yǔ)言實(shí)現(xiàn)微信公眾平臺(tái),雖然不是全部代碼,但是也是給我們提供了一個(gè)非常好的思路,需要的朋友可以參考下2015-01-01
Go語(yǔ)言中Redis緩存與本地內(nèi)存緩存實(shí)戰(zhàn)
在現(xiàn)代高并發(fā)系統(tǒng)中,緩存技術(shù)是提升性能和降低數(shù)據(jù)庫(kù)壓力的關(guān)鍵手段,本文將為大家介紹一下Redis緩存與本地內(nèi)存緩存的具體應(yīng)用,需要的可以了解下2025-03-03
Go語(yǔ)言實(shí)現(xiàn)釘釘發(fā)送通知
本文通過(guò)代碼給大家介紹了Go語(yǔ)言實(shí)現(xiàn)釘釘發(fā)送通知,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11
Go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之插入排序示例詳解
這篇文章主要為大家介紹了Go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之插入排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Go語(yǔ)言學(xué)習(xí)之context包的用法詳解
日常Go開(kāi)發(fā)中,Context包是用的最多的一個(gè)了,幾乎所有函數(shù)的第一個(gè)參數(shù)都是ctx,那么我們?yōu)槭裁匆獋鬟fContext呢,Context又有哪些用法,底層實(shí)現(xiàn)是如何呢?相信你也一定會(huì)有探索的欲望,那么就跟著本篇文章,一起來(lái)學(xué)習(xí)吧2022-10-10

