fix: 维护期间登录一律返回统一提示,不泄露账号状态

- 维护检查提前到 FindByEmail 之后、所有状态检查之前
- 非站长账号(不存在/已封禁/已锁定等)统一走模拟 bcrypt 防时序 → 返回维护提示
- 站长角色正常通过,走完整登录流程
- 旧实现:维护检查在 banned/locked/deleted 之后,会泄露账号状态
This commit is contained in:
2026-05-30 21:10:41 +08:00
parent c1357cf4af
commit fb94ce60ae

View File

@ -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
}