AdoS0TenantScope.cs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. using Microsoft.AspNetCore.Mvc;
  2. namespace Admin.NET.Plugin.AiDOP.Infrastructure;
  3. /// <summary>
  4. /// S0 仓储基础资料的可信租户解析(写/读业务表统一取此值)。
  5. ///
  6. /// 铁律(W-01/02/03 修复):
  7. /// - 租户只来自认证后的 JWT claim(UserManager.TenantId),前端 payload 的 tenantId 一律忽略;
  8. /// - 不使用任何固定默认租户兜底(区别于 QmsTenantScope.Current() 会回落 DefaultTenantId);
  9. /// - 无有效租户(无 token → App.User 空 → TenantId=0)→ 拒绝;
  10. /// - 超级管理员未显式选择目标租户(其 claim 即自身主租户 <see cref="MainTenantId"/>)→ 拒绝,
  11. /// 必须在登录时选择目标租户(SysAuthService 支持超管登录传 tenantId 覆盖)。
  12. /// </summary>
  13. public static class AdoS0TenantScope
  14. {
  15. /// <summary>主/系统租户(超管自身归属,== QmsTenantScope.DefaultTenantId)。仅用作"超管未选租户"的拒绝哨兵,绝不作为业务数据归属。</summary>
  16. public const long MainTenantId = 1300000000001L;
  17. /// <summary>
  18. /// 解析当前请求的可信目标租户。成功返回 true 且 tenantId 为非空非 0 的有效租户;
  19. /// 失败返回 false 且 error 为明确业务错误(调用方直接 return error)。
  20. /// </summary>
  21. public static bool TryResolveRequired(out long tenantId, out IActionResult? error)
  22. {
  23. tenantId = 0;
  24. error = null;
  25. var um = App.GetRequiredService<UserManager>();
  26. var tid = um.TenantId; // 认证后的 JWT claim;无 token 时为 0
  27. if (tid <= 0)
  28. {
  29. error = AdoS0ApiErrors.InvalidRequest("无法确定当前租户,请重新登录或选择目标租户");
  30. return false;
  31. }
  32. // 超管的 claim 若等于自身主租户,视为"未选择目标租户"(不允许自动用超管自身/固定默认租户写业务数据)
  33. if (um.SuperAdmin && tid == MainTenantId)
  34. {
  35. error = AdoS0ApiErrors.InvalidRequest("超级管理员操作前必须选择目标租户");
  36. return false;
  37. }
  38. tenantId = tid;
  39. return true;
  40. }
  41. /// <summary>
  42. /// Service 层(无 IActionResult 上下文,如取号/编号服务)统一的可信租户解析。
  43. /// 语义与 <see cref="TryResolveRequired"/> 完全一致(同一 JWT 来源、同样拒绝无租户/超管未选),
  44. /// 但解析失败时抛出业务异常而非返回错误结果。绝不回退固定默认租户、绝不读前端 tenantId。
  45. /// </summary>
  46. public static long ResolveRequiredOrThrow()
  47. {
  48. var um = App.GetRequiredService<UserManager>();
  49. var tid = um.TenantId;
  50. if (tid <= 0)
  51. throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
  52. if (um.SuperAdmin && tid == MainTenantId)
  53. throw Oops.Oh("超级管理员操作前必须选择目标租户");
  54. return tid;
  55. }
  56. }