功能:扩展云管资源运维、账号授权及监控管理
This commit is contained in:
1 parent
eef485306f
commit
6134d49c16
87 files changed
+29498
-126
No files matched your search
@@ -0,0 +1,59 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"github.com/gin-gonic/gin"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Count attempts before password hashing. Shared storage keeps the budget across
|
||||
// API replicas and restarts; neither passwords nor cleartext account/IP keys persist.
|
||||
func (s *Server) allowLoginAttempt(c *gin.Context, username string) bool {
|
||||
tx, err := s.DB.BeginTxx(c.Request.Context(), nil)
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return false
|
||||
}
|
||||
defer tx.Rollback()
|
||||
retry := 0
|
||||
for _, limit := range []struct {
|
||||
key string
|
||||
max int
|
||||
}{{"account:" + strings.ToLower(strings.TrimSpace(username)), 10}, {"ip:" + c.ClientIP(), 30}} {
|
||||
mac := hmac.New(sha256.New, []byte(s.Config.JWTSecret))
|
||||
mac.Write([]byte("login-limit:" + limit.key))
|
||||
key := hex.EncodeToString(mac.Sum(nil))
|
||||
var row struct {
|
||||
Attempts int `db:"attempts"`
|
||||
Started time.Time `db:"window_started"`
|
||||
}
|
||||
err = tx.GetContext(c.Request.Context(), &row, `INSERT INTO login_limits(key,attempts) VALUES($1,1)
|
||||
ON CONFLICT(key) DO UPDATE SET attempts=CASE WHEN login_limits.window_started<now()-interval '5 minutes' THEN 1 ELSE least(login_limits.attempts+1,$2+1) END,
|
||||
window_started=CASE WHEN login_limits.window_started<now()-interval '5 minutes' THEN now() ELSE login_limits.window_started END RETURNING attempts,window_started`, key, limit.max)
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return false
|
||||
}
|
||||
if row.Attempts > limit.max {
|
||||
seconds := int(math.Ceil(time.Until(row.Started.Add(5 * time.Minute)).Seconds()))
|
||||
if seconds > retry {
|
||||
retry = seconds
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
s.dbError(c, err)
|
||||
return false
|
||||
}
|
||||
if retry > 0 {
|
||||
c.Header("Retry-After", strconv.Itoa(retry))
|
||||
s.fail(c, 429, "login_rate_limited", "登录尝试过于频繁,请在 "+strconv.Itoa(retry)+" 秒后重试")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in new issue
Block a user