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;
}
}
}