@@ -0,0 +1,105 @@
|
||||
package limitHandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/constant"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/web/webg"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var limitURI = []string{
|
||||
"/api/web/admin/login",
|
||||
"/api/web/admin/slogin",
|
||||
"/api/web/admin/verify",
|
||||
}
|
||||
var (
|
||||
tgBotToken = "2013237388:AAFwvKvEmQfEvcI7wt_64Zkp9KPfgHo8sow"
|
||||
tgGroupId = "-1003735177636"
|
||||
)
|
||||
var (
|
||||
limitDuration = 3 * time.Minute
|
||||
limitCount = 5
|
||||
)
|
||||
|
||||
var memCache = cache.New(1*time.Minute, 10*time.Minute)
|
||||
|
||||
// LoginLimit 登陆频率限制
|
||||
func LoginLimit(ctx *gin.Context) {
|
||||
path := ctx.Request.URL.Path
|
||||
// 检查是否需要限制频率
|
||||
check := false
|
||||
for _, p := range limitURI {
|
||||
if strings.HasPrefix(path, p) {
|
||||
check = true
|
||||
}
|
||||
}
|
||||
if !check {
|
||||
return
|
||||
}
|
||||
// 测试环境不限制
|
||||
if webg.Conf.Base.Env != constant.ProdEnv {
|
||||
return
|
||||
}
|
||||
// 获取KEY:只按 path + 管理员账号(name),不含 IP
|
||||
// (对方会伪造 IP,若 key 含 IP 则每次换 IP 就重置计数,导致限流被绕过一直重试)
|
||||
ip := ctx.ClientIP() // ip 仅用于超限预警展示,不参与限流 key
|
||||
key := fmt.Sprintf("web-limit:%s", path)
|
||||
// 检查参数值
|
||||
var params map[string]interface{}
|
||||
if ctx.Request.Method == http.MethodPost {
|
||||
raw, _ := ctx.GetRawData()
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(raw))
|
||||
if err := json.Unmarshal(raw, ¶ms); err == nil {
|
||||
if a, ok := params["name"]; ok { // 做登陆频率限制只检查管理员账号
|
||||
key += ":" + a.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查频率
|
||||
aCount, ok := memCache.Get(key)
|
||||
if !ok {
|
||||
memCache.Set(key, 1, limitDuration)
|
||||
return
|
||||
}
|
||||
count, ok2 := aCount.(int)
|
||||
if !ok2 {
|
||||
memCache.Set(key, 1, limitDuration)
|
||||
return
|
||||
}
|
||||
count++
|
||||
defer func() { memCache.Set(key, count, limitDuration) }()
|
||||
if count > limitCount {
|
||||
// 超出限制
|
||||
go func() {
|
||||
// 预警
|
||||
message := fmt.Sprintf("[91Porn]\n登陆请求频率过高!\n时间: %s\n请求地址: %s\n请求参数: %+v\nIP: %s\n错误次数: %d", time.Now().Format("2006-01-02 15:04:05"), path, params, ip, count)
|
||||
// 请求地址/参数为不可信输入,去掉非法 UTF-8 字节;不用 parse_mode,避免 Markdown 把内容解析成实体后报错
|
||||
message = strings.ToValidUTF8(message, "")
|
||||
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", tgBotToken)
|
||||
req := map[string]interface{}{
|
||||
"chat_id": tgGroupId,
|
||||
"text": message,
|
||||
}
|
||||
resp, _ := httputil.DefaultClientPostJson(url, nil, req)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
ctx.Abort()
|
||||
common.ServeJSON(ctx, stderr.ErrReqForbidden, nil)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
Reference in New Issue
Block a user