using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.AspNetCore.Http; using Volo.Abp.MultiTenancy; using Volo.Abp.Security.Claims; using Yi.Framework.Rbac.Domain.Shared.Consts; using Yi.Framework.SqlSugarCore.Abstractions; namespace FoodLabeling.Application.Helpers; /// /// SaaS 业务租户上下文判定(平台主库 / Default 租户不走公司菜单子集过滤)。 /// public static class TenantBusinessContextHelper { private static readonly Guid ProtectedDefaultTenantId = Guid.Parse("11111111-1111-1111-1111-111111111111"); /// /// 解析当前请求的业务租户 Id:优先 ,其次 JWT / Authorization Claim。 /// public static Guid? ResolveBusinessTenantId(ICurrentTenant? currentTenant, HttpContext? httpContext) { if (currentTenant?.Id is { } fromContext && fromContext != Guid.Empty && fromContext != ProtectedDefaultTenantId) { return fromContext; } var fromJwt = TryGetTenantIdFromHttpContext(httpContext); if (fromJwt.HasValue && fromJwt.Value != Guid.Empty && fromJwt.Value != ProtectedDefaultTenantId) { return fromJwt; } return null; } /// /// 当前请求是否处于真实公司业务租户上下文(须按 fl_th_tenant_menu_permission 限定可分配菜单)。 /// public static bool ShouldScopeMenusByCompany( DbConnOptions options, ICurrentTenant? currentTenant, HttpContext? httpContext) { return ShouldScopeMenusByCompany(options, ResolveBusinessTenantId(currentTenant, httpContext)); } /// /// 当前请求是否处于真实公司业务租户上下文(须按 fl_th_tenant_menu_permission 限定可分配菜单)。 /// public static bool ShouldScopeMenusByCompany(DbConnOptions options, Guid? tenantId) { return options.EnabledSaasMultiTenancy && tenantId.HasValue && tenantId.Value != Guid.Empty && tenantId.Value != ProtectedDefaultTenantId; } /// /// 从 HttpContext Principal 或 Authorization Bearer JWT 读取 TenantId Claim 原始值。 /// public static string? TryGetTenantIdClaimValue(HttpContext? httpContext) { if (httpContext is null) { return null; } return TryGetTenantIdFromPrincipal(httpContext.User) ?? TryGetTenantIdFromAuthorizationHeader(httpContext.Request.Headers.Authorization.ToString()); } private static Guid? TryGetTenantIdFromHttpContext(HttpContext? httpContext) { var tenantClaim = TryGetTenantIdClaimValue(httpContext); if (string.IsNullOrWhiteSpace(tenantClaim) || !Guid.TryParse(tenantClaim, out var tenantGuid) || tenantGuid == Guid.Empty) { return null; } return tenantGuid; } private static string? TryGetTenantIdFromPrincipal(ClaimsPrincipal? user) { if (user?.Identity?.IsAuthenticated != true) { return null; } return user.FindFirst(TokenTypeConst.TenantId)?.Value ?? user.FindFirst(AbpClaimTypes.TenantId)?.Value ?? user.Claims.FirstOrDefault(c => c.Type.EndsWith("tenantId", StringComparison.OrdinalIgnoreCase) || c.Type.Equals("tenantid", StringComparison.OrdinalIgnoreCase))?.Value; } /// /// 多租户中间件可能早于 JWT Principal 就绪;直接从 Authorization 解析 TenantId Claim。 /// private static string? TryGetTenantIdFromAuthorizationHeader(string? authorization) { if (string.IsNullOrWhiteSpace(authorization)) { return null; } const string bearerPrefix = "Bearer "; if (!authorization.StartsWith(bearerPrefix, StringComparison.OrdinalIgnoreCase)) { return null; } var jwt = authorization[bearerPrefix.Length..].Trim(); if (string.IsNullOrWhiteSpace(jwt)) { return null; } try { var token = new JwtSecurityTokenHandler().ReadJwtToken(jwt); return token.Claims.FirstOrDefault(c => c.Type == TokenTypeConst.TenantId || c.Type == AbpClaimTypes.TenantId || c.Type.EndsWith("tenantId", StringComparison.OrdinalIgnoreCase)) ?.Value; } catch (Exception) { return null; } } }