|
|
@@ -106,6 +106,14 @@ func (l *LoginLimiter) VerifyCaptcha(ip, code string) bool {
|
|
106
|
106
|
return false
|
|
107
|
107
|
}
|
|
108
|
108
|
|
|
|
109
|
+// RemoveCaptcha 移除指定 IP 的验证码
|
|
|
110
|
+func (l *LoginLimiter) RemoveCaptcha(ip string) {
|
|
|
111
|
+ l.mu.Lock()
|
|
|
112
|
+ defer l.mu.Unlock()
|
|
|
113
|
+
|
|
|
114
|
+ delete(l.captchas, ip)
|
|
|
115
|
+}
|
|
|
116
|
+
|
|
109
|
117
|
// CleanupExpired 清理过期的记录
|
|
110
|
118
|
func (l *LoginLimiter) CleanupExpired() {
|
|
111
|
119
|
l.mu.Lock()
|
|
|
@@ -120,6 +128,7 @@ func (l *LoginLimiter) CleanupExpired() {
|
|
120
|
128
|
}
|
|
121
|
129
|
}
|
|
122
|
130
|
}
|
|
|
131
|
+
|
|
123
|
132
|
func (l *LoginLimiter) RemoveRecord(ip string) {
|
|
124
|
133
|
l.mu.Lock()
|
|
125
|
134
|
defer l.mu.Unlock()
|
|
|
@@ -162,7 +171,7 @@ func (ct *Login) Login(c *gin.Context) {
|
|
162
|
171
|
// 检查是否需要验证码
|
|
163
|
172
|
if loginLimiter.NeedsCaptcha(clientIp) {
|
|
164
|
173
|
if f.Captcha == "" {
|
|
165
|
|
- response.Fail(c, 110, response.TranslateMsg(c, "CaptchaRequired"))
|
|
|
174
|
+ response.Fail(c, 110, response.TranslateMsg(c, "CaptchaError"))
|
|
166
|
175
|
return
|
|
167
|
176
|
}
|
|
168
|
177
|
if !loginLimiter.VerifyCaptcha(clientIp, f.Captcha) {
|
|
|
@@ -176,6 +185,12 @@ func (ct *Login) Login(c *gin.Context) {
|
|
176
|
185
|
if u.Id == 0 {
|
|
177
|
186
|
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
|
|
178
|
187
|
loginLimiter.RecordFailure(clientIp)
|
|
|
188
|
+ if loginLimiter.NeedsCaptcha(clientIp) {
|
|
|
189
|
+ // 移除原验证码,重新生成
|
|
|
190
|
+ loginLimiter.RemoveCaptcha(clientIp)
|
|
|
191
|
+ response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
|
|
192
|
+ return
|
|
|
193
|
+ }
|
|
179
|
194
|
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
|
|
180
|
195
|
return
|
|
181
|
196
|
}
|