106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
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()
|
|
}
|