fetchInitialCredentialsLogic.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. package product
  2. import (
  3. "context"
  4. "encoding/json"
  5. authHelper "perms-system-server/internal/logic/auth"
  6. "perms-system-server/internal/response"
  7. "perms-system-server/internal/svc"
  8. "perms-system-server/internal/types"
  9. "github.com/zeromicro/go-zero/core/logx"
  10. )
  11. type FetchInitialCredentialsLogic struct {
  12. logx.Logger
  13. ctx context.Context
  14. svcCtx *svc.ServiceContext
  15. }
  16. func NewFetchInitialCredentialsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FetchInitialCredentialsLogic {
  17. return &FetchInitialCredentialsLogic{
  18. Logger: logx.WithContext(ctx),
  19. ctx: ctx,
  20. svcCtx: svcCtx,
  21. }
  22. }
  23. // FetchInitialCredentials 凭 CreateProduct 响应中的 credentialsTicket 一次性领取 appSecret 与初始
  24. // adminPassword。Ticket 在 Redis 中以 initialCredentialsKeyPrefix 前缀保存,短 TTL(5 分钟),
  25. // 一次消费后立即删除;调用此接口的通常是 CreateProduct 的直接后继,因此要求超管身份——即便 ticket
  26. // 泄漏到日志,非超管也没法消费,进一步压缩攻击面(审计 M-4)。
  27. func (l *FetchInitialCredentialsLogic) FetchInitialCredentials(req *types.FetchInitialCredentialsReq) (*types.FetchInitialCredentialsResp, error) {
  28. if err := authHelper.RequireSuperAdmin(l.ctx); err != nil {
  29. return nil, err
  30. }
  31. if req == nil || req.Ticket == "" {
  32. return nil, response.ErrBadRequest("ticket 不能为空")
  33. }
  34. key := initialCredentialsKeyPrefix + req.Ticket
  35. // GetDelCtx 语义 = GET + DEL 原子化,确保一次性消费:即便并发两次请求同一 ticket,只有一次
  36. // 能拿到非空返回,另一次被识别为已消费/已过期。
  37. val, err := l.svcCtx.Redis.GetDelCtx(l.ctx, key)
  38. if err != nil {
  39. logx.WithContext(l.ctx).Errorf("FetchInitialCredentials: redis getdel failed: %v", err)
  40. return nil, response.NewCodeError(503, "凭证服务暂时不可用,请稍后重试")
  41. }
  42. if val == "" {
  43. return nil, response.ErrBadRequest("凭证票据无效或已过期")
  44. }
  45. var payload initialCredentialsPayload
  46. if err := json.Unmarshal([]byte(val), &payload); err != nil {
  47. logx.WithContext(l.ctx).Errorf("FetchInitialCredentials: unmarshal payload failed: %v", err)
  48. return nil, response.NewCodeError(500, "凭证数据异常,请联系管理员")
  49. }
  50. return &types.FetchInitialCredentialsResp{
  51. AppKey: payload.AppKey,
  52. AppSecret: payload.AppSecret,
  53. AdminUser: payload.AdminUser,
  54. AdminPassword: payload.AdminPassword,
  55. }, nil
  56. }