IP 訪問限制及提交次數,用 golang 如何實現 ?

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

實現步驟

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

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

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

核心概念

使用 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 客戶端初始化
  1. IP 獲取
  1. Rate Limiting 中間件
  1. 處理請求

改進與擴展

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

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

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

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

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

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

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ZGMLELQ3FCZ-q0LECSIbgQ