This commit is contained in:
2026-08-15 01:28:51 +08:00
parent 6698e59c3a
commit 2f165d3a45
26 changed files with 242 additions and 216 deletions
+18 -17
View File
@@ -19,7 +19,8 @@ import (
)
type Public struct {
system *service.SystemService
auth *service.AuthService
system *service.SystemConfigService
settings *service.SettingsService
audit *service.AuditService
scheduler *worker.TaskScheduler
@@ -27,8 +28,8 @@ type Public struct {
store *captchaStore
}
func NewPublic(runtime *conf.Runtime, system *service.SystemService, settings *service.SettingsService, audit *service.AuditService, scheduler *worker.TaskScheduler) *Public {
return &Public{system: system, settings: settings, audit: audit, scheduler: scheduler, runtime: runtime, store: &captchaStore{service: system, runtime: runtime}}
func NewPublic(runtime *conf.Runtime, auth *service.AuthService, system *service.SystemConfigService, settings *service.SettingsService, audit *service.AuditService, scheduler *worker.TaskScheduler) *Public {
return &Public{auth: auth, system: system, settings: settings, audit: audit, scheduler: scheduler, runtime: runtime, store: &captchaStore{service: settings, runtime: runtime}}
}
func (h *Public) captchaConfig() (int, int, int) {
@@ -55,7 +56,7 @@ func (h *Public) Captcha(c *gin.Context) {
if security != nil {
keyLong, width, height = security.KeyLong, security.ImgWidth, security.ImgHeight
ttl := time.Duration(security.CaptchaTimeout) * time.Second
failures, _ := ensureLoginIPCounter(c.Request.Context(), h.system, c.ClientIP(), ttl)
failures, _ := ensureLoginIPCounter(c.Request.Context(), h.settings, c.ClientIP(), ttl)
openCaptcha = security.CaptchaOpen == 0 || failures > security.CaptchaOpen
}
driver := base64Captcha.NewDriverDigit(height, width, keyLong, 0.7, 80)
@@ -75,7 +76,7 @@ func (h *Public) Login(c *gin.Context) {
}
security, _ := h.settings.CurrentSecurity(c.Request.Context())
if security != nil && security.LockEnable {
if _, locked, _ := h.system.CacheGet(c.Request.Context(), "login:lock:"+req.Username); locked {
if _, locked, _ := h.settings.CacheGet(c.Request.Context(), "login:lock:"+req.Username); locked {
httpx.Fail(c, "账号已锁定,请 "+strconv.Itoa(security.LockDuration)+" 分钟后再试")
_ = h.audit.RecordLoginRequest(c.Request.Context(), &dto.LoginLogRequest{Username: req.Username, IP: c.ClientIP(), Status: false, ErrorMessage: "账号已锁定", Agent: c.Request.UserAgent()})
return
@@ -87,18 +88,18 @@ func (h *Public) Login(c *gin.Context) {
if security.CaptchaTimeout > 0 {
ipTTL = time.Duration(security.CaptchaTimeout) * time.Second
}
failures, _ := ensureLoginIPCounter(c.Request.Context(), h.system, c.ClientIP(), ipTTL)
failures, _ := ensureLoginIPCounter(c.Request.Context(), h.settings, c.ClientIP(), ipTTL)
requireCaptcha = security.CaptchaOpen == 0 || failures > security.CaptchaOpen
}
if requireCaptcha && (req.CaptchaID == "" || req.Captcha == "" || !h.store.Verify(req.CaptchaID, req.Captcha, true)) {
_, _ = h.system.CacheIncrement(c.Request.Context(), c.ClientIP(), ipTTL)
_, _ = h.settings.CacheIncrement(c.Request.Context(), c.ClientIP(), ipTTL)
_ = h.audit.RecordLoginRequest(c.Request.Context(), &dto.LoginLogRequest{Username: req.Username, IP: c.ClientIP(), Status: false, ErrorMessage: "验证码错误", Agent: c.Request.UserAgent()})
httpx.Fail(c, "验证码错误")
return
}
result, err := h.system.Login(c.Request.Context(), req.Username, req.Password)
result, err := h.auth.Login(c.Request.Context(), req.Username, req.Password)
if err != nil {
_, _ = h.system.CacheIncrement(c.Request.Context(), c.ClientIP(), ipTTL)
_, _ = h.settings.CacheIncrement(c.Request.Context(), c.ClientIP(), ipTTL)
if errors.Is(err, biz.ErrUserDisabled) {
var disabled *service.UserDisabledError
errors.As(err, &disabled)
@@ -113,9 +114,9 @@ func (h *Public) Login(c *gin.Context) {
_ = h.audit.RecordLoginRequest(c.Request.Context(), &dto.LoginLogRequest{Username: req.Username, IP: c.ClientIP(), Status: false, ErrorMessage: "用户名不存在或者密码错误", Agent: c.Request.UserAgent()})
if security != nil && security.LockEnable {
lockTTL := time.Duration(security.LockDuration) * time.Minute
failures, _ := h.system.CacheIncrement(c.Request.Context(), "login:fail:"+req.Username, lockTTL)
failures, _ := h.settings.CacheIncrement(c.Request.Context(), "login:fail:"+req.Username, lockTTL)
if int(failures) >= security.LockThreshold {
_ = h.system.CacheSet(c.Request.Context(), "login:lock:"+req.Username, "1", lockTTL)
_ = h.settings.CacheSet(c.Request.Context(), "login:lock:"+req.Username, "1", lockTTL)
}
}
httpx.Fail(c, "用户名不存在或者密码错误")
@@ -123,15 +124,15 @@ func (h *Public) Login(c *gin.Context) {
}
userID, _ := result.User["ID"].(uint)
_ = h.audit.RecordLoginRequest(c.Request.Context(), &dto.LoginLogRequest{Username: req.Username, IP: c.ClientIP(), Status: true, Agent: c.Request.UserAgent(), UserID: userID})
_ = h.system.CacheDelete(c.Request.Context(), "login:fail:"+req.Username)
_ = h.system.CacheDelete(c.Request.Context(), "login:lock:"+req.Username)
_ = h.settings.CacheDelete(c.Request.Context(), "login:fail:"+req.Username)
_ = h.settings.CacheDelete(c.Request.Context(), "login:lock:"+req.Username)
maxAge := int(time.Until(time.UnixMilli(result.ExpiresAt)).Seconds())
httpx.SetTokenCookie(c, result.Token, maxAge)
httpx.Write(c, httpx.CodeSuccess, result, "登录成功")
}
func ensureLoginIPCounter(ctx context.Context, system *service.SystemService, ip string, expiration time.Duration) (int, error) {
value, exists, err := system.CacheGet(ctx, ip)
func ensureLoginIPCounter(ctx context.Context, settings *service.SettingsService, ip string, expiration time.Duration) (int, error) {
value, exists, err := settings.CacheGet(ctx, ip)
if err != nil {
return 0, err
}
@@ -141,7 +142,7 @@ func ensureLoginIPCounter(ctx context.Context, system *service.SystemService, ip
if expiration <= 0 {
expiration = time.Hour
}
if err = system.CacheSet(ctx, ip, "1", expiration); err != nil {
if err = settings.CacheSet(ctx, ip, "1", expiration); err != nil {
return 0, err
}
return 1, nil
@@ -194,7 +195,7 @@ func (h *Public) InitializeDatabase(engine *gin.Engine) gin.HandlerFunc {
}
type captchaStore struct {
service *service.SystemService
service *service.SettingsService
runtime *conf.Runtime
}