切換語言為:簡體

基於golang實現IP訪問限制及提交次數

  • 爱糖宝
  • 2024-10-21
  • 2059
  • 0
  • 0

在 Web 應用中,通常會需要對 IP 訪問進行限制以及控制提交次數,以防止惡意攻擊(例如暴力破解、DoS攻擊、API濫用等)。爲了實現這一功能,我們可以結合 Golang 的特性,使用中介軟體或者基於 Redis 這樣的快取服務來實現 IP 限制和提交次數的控制。

實現步驟

  1. IP 訪問限制:對每個 IP 的訪問頻次進行限制,比如每個 IP 每分鐘只能訪問某個介面 10 次。超過限制後,返回錯誤資訊(例如 429 Too Many Requests)。

  2. 提交次數限制:透過限制某個時間段內某個 IP 的提交次數,防止暴力破解或者濫用介面。

  3. Redis(或其他儲存系統)作為計數器:爲了更好地實現這種限制,可以使用 Redis 等快取系統來儲存 IP 的訪問記錄、提交次數等,因為 Redis 的效能和易用性使它成為理想的選擇。

核心概念

  • Rate Limiting(限流):根據 IP 限制某個時間段內的訪問次數。

  • 請求次數計數:對每個 IP 進行計數,並基於計數來判斷是否超過限制。

  • 時間視窗:設定一定的時間視窗(例如一分鐘或五分鐘),在這個時間段內統計 IP 的訪問次數。

使用 Golang 及 Redis 實現 IP 訪問限制和提交次數限制

這裏我們使用 Redis 來儲存和控制訪問次數,並結合 Go 實現一個簡單的 IP 訪問限制中介軟體。

依賴庫

你可以使用 Redis 官方的 Go 客戶端 go-redis 來連線 Redis 進行操作。先安裝這個庫:

go get github.com/go-redis/redis/v8

實現程式碼

下面的程式碼演示瞭如何使用 Redis 來實現 IP 訪問限制和提交次數限制。

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 資料庫
	})
}

// 獲取客戶端的 IP 地址
func getIP(r *http.Request) string {
	// 嘗試從 X-Forwarded-For 或 X-Real-IP 獲取真實 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 訪問限制
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 中的訪問次數
		count, err := rdb.Get(ctx, key).Result()
		if err == redis.Nil {
			// 如果沒有記錄,設定計數為1,並設定過期時間
			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 {
			// 將訪問次數轉換為整數
			countInt, _ := strconv.Atoi(count)
			if countInt >= 10 { // 假設限制為每分鐘最多10次
				http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
				return
			}

			// 遞增計數
			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()

	// 建立 HTTP 伺服器並新增中介軟體
	http.HandleFunc("/submit", rateLimitMiddleware(submitHandler))

	log.Println("Server is running on port 8080...")
	http.ListenAndServe(":8080", nil)
}

程式碼解析

  1. Redis 客戶端初始化

    • 使用 redis.NewClient() 初始化 Redis 客戶端。

    • 透過 rdb.Set()rdb.Get() 來操作 Redis 中的計數器。

  2. IP 獲取

    • 透過 getIP() 函式獲取請求的客戶端 IP 地址。該函式嘗試從請求頭中的 X-Forwarded-ForX-Real-IP 獲取真實的 IP。如果沒有,則使用 RemoteAddr

  3. Rate Limiting 中介軟體

    • rateLimitMiddleware() 是核心的中介軟體函式,負責限制每個 IP 的訪問次數。它使用 Redis 來儲存每個 IP 的訪問計數和限流時間視窗(這裏設定為 1 分鐘)。

    • 當 IP 的訪問次數超過限制時,返回 HTTP 狀態碼 429 Too Many Requests

  4. 處理請求

    • submitHandler() 是一個簡單的示例處理器,處理成功的請求。

    • 訪問 /submit 時,經過中介軟體限制後,正常情況下返回 "Request successful"。

改進與擴充套件

  1. 動態調整限流策略: 可以根據不同的使用者型別、不同的 API 路徑動態調整限流策略。例如,VIP 使用者可能會有更高的訪問頻次。

  2. IP 黑名單: 透過 Redis 或其他儲存系統維護一個黑名單,遇到黑名單中的 IP 可以直接拒絕請求。

  3. 按時間視窗的限流演算法: 你可以採用滑動視窗、漏桶演算法、令牌桶演算法等更復雜的限流演算法來實現更靈活的控制。

  4. 使用 Redis Expire 特性: 在 Redis 中使用 SetEX(帶過期時間的鍵設定)或 TTL 來確保計數器可以自動重置,避免手動管理。

  5. 日誌記錄與報警: 可以結合日誌系統,在某個 IP 頻繁觸發限制時記錄日誌或傳送報警資訊。

透過 Golang 和 Redis 的結合,可以輕鬆實現 IP 訪問限制和提交次數控制。Redis 的高效能特性使其非常適合用作限流計數器的儲存。在實際應用中,可以根據需要擴充套件該方案,例如使用不同的限流演算法、結合 IP 黑名單等。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.