feat: 新增管理员用户管理功能与账号封禁体系
此提交完成了全站管理员后台核心功能开发: 1. 新增AdminUserService与管理接口,支持用户列表查询、角色变更与封禁解封 2. 重构认证链路,统一账号封禁错误码与处理逻辑 3. 新增全局强制下线机制,账号被封禁时自动弹出告知弹窗并清理登录态 4. 重构管理员后台路由与布局,统一权限校验逻辑 5. 优化通知轮询逻辑,页面切回前台时立即刷新未读计数 6. 合并原公告、外观管理页到统一后台面板
This commit is contained in:
@@ -126,7 +126,7 @@ func (s *AuthService) Login(username, password string) (string, string, *model.U
|
||||
}
|
||||
// 封禁判定放在密码比较之后,同样避免时序差异
|
||||
if user.Banned {
|
||||
return "", "", nil, errors.New("账号已被封禁")
|
||||
return "", "", nil, ErrAccountBanned
|
||||
}
|
||||
|
||||
accessToken, err := s.generateToken(&user)
|
||||
@@ -184,13 +184,14 @@ func (s *AuthService) ValidateClaims(claims *UserClaims) (*model.User, error) {
|
||||
if err := s.db.First(&user, claims.ID).Error; err != nil {
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
// 封禁优先于版本判定:封禁必然伴随 token_version 递增,
|
||||
// 但前端需要明确知道"被封禁"而非笼统的登录失效
|
||||
if user.Banned {
|
||||
return nil, ErrAccountBanned
|
||||
}
|
||||
// token 版本不匹配 → 已被撤销(改密码/封禁/管理员操作)
|
||||
if user.TokenVersion != claims.TokenVersion {
|
||||
return nil, errors.New("token 已失效")
|
||||
}
|
||||
// 实时校验封禁状态(不依赖 JWT 中的缓存值)
|
||||
if user.Banned {
|
||||
return nil, errors.New("账号已被封禁")
|
||||
return nil, ErrTokenRevoked
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
@@ -218,6 +219,14 @@ func generateRandomToken() string {
|
||||
return base64.URLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// 账号级登录态错误:前端据此区分"被封禁强制下线"与普通登录过期
|
||||
var (
|
||||
// ErrAccountBanned 账号已被封禁(登录/刷新/鉴权全链路统一返回,便于前端识别并告知用户)
|
||||
ErrAccountBanned = errors.New("账号已被封禁")
|
||||
// ErrTokenRevoked 凭据版本失配:改密/管理员强制下线等导致旧 JWT 立即作废
|
||||
ErrTokenRevoked = errors.New("登录态已失效")
|
||||
)
|
||||
|
||||
// refresh token 相关错误
|
||||
var (
|
||||
ErrRefreshInvalid = errors.New("refresh token 无效")
|
||||
@@ -299,7 +308,7 @@ func (s *AuthService) loadActiveUser(tx *gorm.DB, userID uint) (*model.User, err
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
if user.Banned {
|
||||
return nil, errors.New("账号已被封禁")
|
||||
return nil, ErrAccountBanned
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
@@ -311,6 +320,12 @@ func (s *AuthService) ValidateRefreshToken(token string) (*model.User, error) {
|
||||
return nil, ErrRefreshInvalid
|
||||
}
|
||||
if rt.Revoked {
|
||||
// 撤销常源于封禁/改密:补查封禁状态,让被封禁用户的刷新请求
|
||||
// 得到可识别的原因(前端据此弹封禁提示,而非笼统的登录过期)
|
||||
var owner model.User
|
||||
if err := s.db.Select("banned").First(&owner, rt.UserID).Error; err == nil && owner.Banned {
|
||||
return nil, ErrAccountBanned
|
||||
}
|
||||
return nil, errors.New("refresh token 已撤销")
|
||||
}
|
||||
if time.Now().After(rt.ExpiresAt) {
|
||||
|
||||
Reference in New Issue
Block a user