| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- // Code scaffolded by goctl. Safe to edit.
- // goctl 1.10.0
- package auth
- import (
- "context"
- "errors"
- "fmt"
- "perms-system-server/internal/loaders"
- "perms-system-server/internal/middleware"
- userModel "perms-system-server/internal/model/user"
- "perms-system-server/internal/response"
- "perms-system-server/internal/svc"
- "github.com/zeromicro/go-zero/core/limit"
- "github.com/zeromicro/go-zero/core/logx"
- )
- type LogoutLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
- func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
- return &LogoutLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
- // Logout 用户注销。递增当前用户的 tokenVersion 使所有已签发的 access/refresh 令牌立即失效,并清除用户缓存。
- func (l *LogoutLogic) Logout() error {
- // 审计 M-R11-2:从 middleware 的 UserDetails 直接取 userId/username,
- // 不再到 IncrementTokenVersion 内部再 FindOne 一次仅为构造缓存键。
- ud := middleware.GetUserDetails(l.ctx)
- if ud == nil || ud.UserId == 0 {
- return response.ErrUnauthorized("未登录")
- }
- userId := ud.UserId
- if l.svcCtx.TokenOpLimiter != nil {
- code, _ := l.svcCtx.TokenOpLimiter.Take(fmt.Sprintf("logout:%d", userId))
- if code == limit.OverQuota {
- return response.ErrTooManyRequests("操作过于频繁,请稍后再试")
- }
- }
- if _, err := l.svcCtx.SysUserModel.IncrementTokenVersion(l.ctx, userId, ud.Username); err != nil {
- // 审计 L-R10-3:IncrementTokenVersion 在目标用户已被并发删除时会返 ErrUpdateConflict。
- // Logout 的语义目标本就是"让该账号的旧令牌立即失效",用户已经消失等同语义已达成,
- // 按幂等成功处理并继续清缓存,不要让一次正常的注销因为极罕见的删号竞态回 500。
- if !errors.Is(err, userModel.ErrUpdateConflict) {
- return err
- }
- logx.WithContext(l.ctx).Infof("logout on already-deleted user userId=%d, treated as idempotent success", userId)
- }
- // 审计 L-R13-5 方案 A:Logout 的目的正是让旧令牌立即失效;缓存失效不能因为 HTTP
- // 请求 ctx 被客户端取消而丢失。detached ctx 确保 IncrementTokenVersion 提交后 UD
- // 即使在 5 分钟 TTL 内也能被主动失效。
- cleanCtx, cancel := loaders.DetachCacheCleanCtx(l.ctx)
- defer cancel()
- l.svcCtx.UserDetailsLoader.Clean(cleanCtx, userId)
- return nil
- }
|