121 lines
2.6 KiB
Go
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, ¶ms); 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()
|
|
}
|