feat: 实现登录失败锁定功能
新增了登录失败次数统计和临时锁定机制,包括: 1. 定义登录锁定数据结构和数据库表结构 2. 实现租户级别的锁定状态持久化接口 3. 添加登录速率限制安全策略配置 4. 在登录流程中加入锁定前置检查和失败计数逻辑 5. 提供管理员手动解锁的内部API接口 6. 新增中文本地化的锁定提示文案
This commit is contained in:
@@ -12,6 +12,7 @@ package main
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -1150,9 +1151,29 @@ func (s *Session) login(msg *ClientComMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
// Login failure lockout: reject already-locked accounts up front without
|
||||
// comparing the password. A nil guard means the feature is not applicable.
|
||||
lockGuard := newLoginLockoutGuard(s, msg.Login.Scheme, msg.Login.Secret)
|
||||
if lockGuard != nil {
|
||||
if retryAfter, locked := lockGuard.preCheck(); locked {
|
||||
logs.Info.Printf("s.login: account locked, rejecting tenant:%d account_hash:%s sid:%s", s.tenantID, lockGuard.accountHash, s.sid)
|
||||
s.queueOut(ErrAuthLocked(msg.Id, msg.Timestamp, loginLockedMessage(retryAfter),
|
||||
map[string]any{"what": "auth", "retry_after_seconds": int(retryAfter.Seconds()) + 1}))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rec, challenge, err := handler.Authenticate(
|
||||
auth.AuthContext{TenantID: s.tenantID, RemoteAddr: s.remoteAddr}, msg.Login.Secret)
|
||||
if err != nil {
|
||||
if lockGuard != nil && errors.Is(err, types.ErrFailed) {
|
||||
if retryAfter, lockedNow := lockGuard.recordFailure(); lockedNow {
|
||||
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, err, msg.Timestamp, loginAuditRiskActionLocked)
|
||||
s.queueOut(ErrAuthLocked(msg.Id, msg.Timestamp, loginLockedMessage(retryAfter),
|
||||
map[string]any{"what": "auth", "retry_after_seconds": int(retryAfter.Seconds()) + 1}))
|
||||
return
|
||||
}
|
||||
}
|
||||
recordSessionLoginFailure(s, msg.Login.Scheme, msg.Login.Secret, rec, msg.Id, err, msg.Timestamp)
|
||||
resp := decodeStoreError(err, msg.Id, msg.Timestamp, nil)
|
||||
if resp.Ctrl.Code >= 500 {
|
||||
@@ -1170,6 +1191,11 @@ func (s *Session) login(msg *ClientComMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
// Password accepted: clear the failure counter and any stale lock state.
|
||||
if lockGuard != nil {
|
||||
lockGuard.clear()
|
||||
}
|
||||
|
||||
// If authenticator did not check user state, it returns state "undef". If so, check user state here.
|
||||
if rec.State == types.StateUndefined {
|
||||
rec.State, err = userGetState(s.tenantID, rec.Uid)
|
||||
|
||||
Reference in New Issue
Block a user