feat: 从黄果 NET hgdjnet-server 9bfbafe 导入 HKDJ 官网后端
来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83 项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet 测试目录 /server/hkdj_net,App/Web API 30331/30332 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package limitHandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hkdj-web-server/pub/common"
|
||||
httpUtil "hkdj-web-server/pub/common/http"
|
||||
"hkdj-web-server/pub/common/srvdef"
|
||||
"hkdj-web-server/web/webg"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var limitURI = []string{
|
||||
"/api/web/login/slogin",
|
||||
"/api/web/login/acc",
|
||||
}
|
||||
|
||||
var (
|
||||
tgBotToken = ""
|
||||
tgGroupId = ""
|
||||
)
|
||||
|
||||
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(
|
||||
"[91猎艳]\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, "")
|
||||
|
||||
if tgBotToken == "" || tgGroupId == "" {
|
||||
return
|
||||
}
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user