Files
web_hghl_server/web/middleware/limitHandler/login.go
T
2026-10-26 16:28:14 +08:00

121 lines
2.6 KiB
Go

package limitHandler
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"videoweb/pub/common"
httpUtil "videoweb/pub/common/http"
"videoweb/pub/common/srvdef"
"videoweb/web/webg"
"github.com/gin-gonic/gin"
"github.com/patrickmn/go-cache"
)
var limitURI = []string{
"/api/web/login/slogin",
"/api/web/login/acc",
}
var (
tgBotToken = "2013237388:AAFwvKvEmQfEvcI7wt_64Zkp9KPfgHo8sow"
tgGroupId = "-1003735177636"
)
var (
limitDuration = time.Minute
limitCount = 5
)
var memCache = cache.New(1*time.Minute, 10*time.Minute)
// LoginLimit 登陆频率限制:按账号维度限制,不依赖 ClientIP(可被伪造)。
func LoginLimit(ctx *gin.Context) {
path := ctx.Request.URL.Path
check := false
for _, p := range limitURI {
if strings.HasPrefix(path, p) {
check = true
break
}
}
if !check {
ctx.Next()
return
}
// 测试环境不限制
if webg.Conf.RunMod != "prod" {
ctx.Next()
return
}
var params map[string]interface{}
var acc string
if ctx.Request.Method == http.MethodPost {
raw, _ := ctx.GetRawData()
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(raw))
if err := json.Unmarshal(raw, &params); err == nil {
if a, ok := params["acc"].(string); ok {
acc = strings.TrimSpace(a)
}
}
}
// 无账号时无法按账号限流,交由后续登录逻辑处理参数错误
if acc == "" {
ctx.Next()
return
}
key := fmt.Sprintf("web-limit:%s:%s", path, acc)
count := 1
if aCount, ok := memCache.Get(key); ok {
if n, ok2 := aCount.(int); ok2 {
count = n + 1
}
}
memCache.Set(key, count, limitDuration)
if count > limitCount {
go func(acc, path string, count int, params map[string]interface{}) {
message := fmt.Sprintf(
"[91porn精选]\n登陆请求频率过高!\n时间: %s\n请求地址: %s\n账号: %s\n请求参数: %+v\n错误次数: %d",
time.Now().Format("2006-01-02 15:04:05"),
path,
acc,
params,
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,
}
jsonBytes, err := json.Marshal(req)
if err != nil {
return
}
header := map[string]string{"Content-Type": "application/json"}
resp, _ := httpUtil.Post(url, header, bytes.NewReader(jsonBytes))
_ = resp.Data
}(acc, path, count, params)
common.ServerJSON(ctx, srvdef.ErrReqForbidden, nil)
ctx.Abort()
return
}
ctx.Next()
}