diff --git a/internal/service/auth_service.go b/internal/service/auth_service.go index efc6e87..0e8888d 100644 --- a/internal/service/auth_service.go +++ b/internal/service/auth_service.go @@ -126,13 +126,24 @@ func (s *AuthService) Register(req model.RegisterRequest, regIP string) (string, } // Login 登录 -// 流程:按邮箱查找 → 已注销则验证密码后要求二次确认 → 检查状态 → 验证密码 → 记录登录 IP/时间 → access JWT + refresh JWT -// 防时序攻击:邮箱不存在时仍执行完整 bcrypt 比对 +// 流程:按邮箱查找 → 维护期间非站长统一拦截(模拟 bcrypt 防时序)→ 状态检查 → 验证密码 → 签发 token // rememberMe=true: refresh Cookie 持久化(30天);false: session cookie(关浏览器即清除) -// loginIP: 登录 IP 地址 -// 若用户处于 deleted 状态且密码正确 → 返回 ErrNeedsConfirmRestore(不自动恢复,不签发 token) func (s *AuthService) Login(req model.LoginRequest, loginIP string) (string, string, *model.User, error) { user, err := s.userRepo.FindByEmail(req.Email) + + // 维护期间:非站长一律返回统一提示(模拟 bcrypt 防时序攻击,不区分账号是否存在) + if s.siteSettings.IsMaintenanceEnabled() { + if err != nil { + _ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password)) + return "", "", nil, common.ErrMaintenanceMode + } + if !model.HasMinRole(user.Role, model.RoleOwner) { + _ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password)) + return "", "", nil, common.ErrMaintenanceMode + } + // 站长 → 继续正常登录流程 + } + if err != nil { _ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password)) return "", "", nil, common.ErrInvalidCred @@ -158,11 +169,6 @@ func (s *AuthService) Login(req model.LoginRequest, loginIP string) (string, str return "", "", nil, common.ErrUserBanned } - // 维护期间仅站长可登录 - if s.siteSettings.IsMaintenanceEnabled() && !model.HasMinRole(user.Role, model.RoleOwner) { - return "", "", nil, common.ErrMaintenanceMode - } - if !common.CheckPassword(req.Password, user.PasswordHash) { return "", "", nil, common.ErrInvalidCred }