在 web 应用中,通常会需要对 ip 访问进行限制以及控制提交次数,以防止恶意攻击(例如暴力破解、dos攻击、api滥用等)。为了实现这一功能,我们可以结合 golang 的特性,使用中间件或者基于 redis 这样的缓存服务来实现 ip 限制和提交次数的控制。
实现步骤
- ip 访问限制:对每个 ip 的访问频次进行限制,比如每个 ip 每分钟只能访问某个接口 10 次。超过限制后,返回错误信息(例如 429 too many requests)。
- 提交次数限制:通过限制某个时间段内某个 ip 的提交次数,防止暴力破解或者滥用接口。
- 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)
}
代码解析
redis 客户端初始化:
- 使用
redis.newclient()初始化 redis 客户端。 - 通过
rdb.set()和rdb.get()来操作 redis 中的计数器。
ip 获取:
通过 getip() 函数获取请求的客户端 ip 地址。该函数尝试从请求头中的 x-forwarded-for 或 x-real-ip 获取真实的 ip。如果没有,则使用 remoteaddr。
rate limiting 中间件:
ratelimitmiddleware()是核心的中间件函数,负责限制每个 ip 的访问次数。它使用 redis 来存储每个 ip 的访问计数和限流时间窗口(这里设置为 1 分钟)。- 当 ip 的访问次数超过限制时,返回 http 状态码
429 too many requests。
处理请求:
submithandler()是一个简单的示例处理器,处理成功的请求。- 访问
/submit时,经过中间件限制后,正常情况下返回 "request successful"。
改进与扩展
- 动态调整限流策略: 可以根据不同的用户类型、不同的 api 路径动态调整限流策略。例如,vip 用户可能会有更高的访问频次。
- ip 黑名单: 通过 redis 或其他存储系统维护一个黑名单,遇到黑名单中的 ip 可以直接拒绝请求。
- 按时间窗口的限流算法: 你可以采用滑动窗口、漏桶算法、令牌桶算法等更复杂的限流算法来实现更灵活的控制。
- 使用 redis expire 特性: 在 redis 中使用
setex(带过期时间的键设置)或ttl来确保计数器可以自动重置,避免手动管理。 - 日志记录与报警: 可以结合日志系统,在某个 ip 频繁触发限制时记录日志或发送报警信息。
通过 golang 和 redis 的结合,可以轻松实现 ip 访问限制和提交次数控制。redis 的高性能特性使其非常适合用作限流计数器的存储。在实际应用中,可以根据需要扩展该方案,例如使用不同的限流算法、结合 ip 黑名单等。
到此这篇关于golang实现ip访问限制及提交次数的文章就介绍到这了,更多相关go ip访问限制内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论