Browse Source

Add `Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims`.

pull/18175/head
maliming 2 years ago
parent
commit
9aae95d243
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 7
      framework/Volo.Abp.sln
  2. 27
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims.csproj
  3. 25
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/AbpAspNetCoreAuthenticationJwtBearerDynamicClaimsModule.cs
  4. 10
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributor.cs
  5. 75
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributorCache.cs
  6. 13
      framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributorOptions.cs
  7. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs
  8. 46
      framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs
  9. 31
      framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs
  10. 5
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs
  11. 51
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/RemoteDynamicClaimsPrincipalContributorBase.cs
  12. 55
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/RemoteDynamicClaimsPrincipalContributorCacheBase.cs
  13. 8
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs
  14. 1
      nupkg/common.ps1
  15. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs
  16. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs

7
framework/Volo.Abp.sln

@ -463,6 +463,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp.Tests", "test\Volo.Abp.Imaging.SkiaSharp.Tests\Volo.Abp.Imaging.SkiaSharp.Tests.csproj", "{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp.Tests", "test\Volo.Abp.Imaging.SkiaSharp.Tests\Volo.Abp.Imaging.SkiaSharp.Tests.csproj", "{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims", "src\Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims\Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims.csproj", "{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -1381,6 +1383,10 @@ Global
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.Build.0 = Release|Any CPU {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.Build.0 = Release|Any CPU
{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1614,6 +1620,7 @@ Global
{F19A6E0C-F719-4ED9-A024-14E4B8D40883} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {F19A6E0C-F719-4ED9-A024-14E4B8D40883} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{198683D0-7DC6-40F2-B81B-8E446E70A9DE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {198683D0-7DC6-40F2-B81B-8E446E70A9DE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DFAF8763-D1D6-4EB4-B459-20E31007FE2F} = {447C8A77-E5F0-4538-8687-7383196D04EA} {DFAF8763-D1D6-4EB4-B459-20E31007FE2F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{6F72FFCE-0183-4EF1-80B6-8FC3268E8E99} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

27
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<PackageId>Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReference Include="..\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" />
</ItemGroup>
</Project>

25
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/AbpAspNetCoreAuthenticationJwtBearerDynamicClaimsModule.cs

@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims;
[DependsOn(
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpCachingModule)
)]
public class AbpAspNetCoreAuthenticationJwtBearerDynamicClaimsModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient();
context.Services.AddHttpContextAccessor();
var abpClaimsPrincipalFactoryOptions = context.Services.ExecutePreConfiguredActions<AbpClaimsPrincipalFactoryOptions>();
if (abpClaimsPrincipalFactoryOptions.IsRemoteRefreshEnabled)
{
context.Services.AddTransient<WebRemoteDynamicClaimsPrincipalContributor>();
context.Services.AddTransient<WebRemoteDynamicClaimsPrincipalContributorCache>();
}
}
}

10
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributor.cs

@ -0,0 +1,10 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims;
[DisableConventionalRegistration]
public class WebRemoteDynamicClaimsPrincipalContributor : RemoteDynamicClaimsPrincipalContributorBase<WebRemoteDynamicClaimsPrincipalContributor, WebRemoteDynamicClaimsPrincipalContributorCache>
{
}

75
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributorCache.cs

@ -0,0 +1,75 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims;
public class WebRemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsPrincipalContributorCacheBase<WebRemoteDynamicClaimsPrincipalContributorCache>
{
public const string HttpClientName = nameof(WebRemoteDynamicClaimsPrincipalContributorCache);
protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IHttpContextAccessor HttpContextAccessor { get; }
protected IOptions<WebRemoteDynamicClaimsPrincipalContributorOptions> Options { get; }
public WebRemoteDynamicClaimsPrincipalContributorCache(
IDistributedCache<AbpDynamicClaimCacheItem> cache,
IHttpClientFactory httpClientFactory,
IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions,
IHttpContextAccessor httpContextAccessor,
IOptions<WebRemoteDynamicClaimsPrincipalContributorOptions> options)
: base(abpClaimsPrincipalFactoryOptions)
{
Cache = cache;
HttpClientFactory = httpClientFactory;
HttpContextAccessor = httpContextAccessor;
Options = options;
}
protected async override Task<AbpDynamicClaimCacheItem?> GetCacheAsync(Guid userId, Guid? tenantId = null)
{
return await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
}
protected async override Task RefreshAsync(Guid userId, Guid? tenantId = null)
{
try
{
if (HttpContextAccessor.HttpContext == null)
{
throw new AbpException($"Failed to refresh remote claims for user: {userId} - HttpContext is null!");
}
var authenticateResult = await HttpContextAccessor.HttpContext.AuthenticateAsync(Options.Value.AuthenticationScheme);
if (!authenticateResult.Succeeded)
{
throw new AbpException($"Failed to refresh remote claims for user: {userId} - authentication failed!");
}
var accessToken = authenticateResult.Properties?.GetTokenValue("access_token");
if (accessToken.IsNullOrWhiteSpace())
{
throw new AbpException($"Failed to refresh remote claims for user: {userId} - access_token is null or empty!");
}
var client = HttpClientFactory.CreateClient(HttpClientName);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, AbpClaimsPrincipalFactoryOptions.Value.RemoteRefreshUrl);
requestMessage.SetBearerToken(accessToken);
var response = await client.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
}
catch (Exception e)
{
Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}");
throw;
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims/Volo/Abp/AspNetCore/Authentication/JwtBearer/DynamicClaims/WebRemoteDynamicClaimsPrincipalContributorOptions.cs

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims;
public class WebRemoteDynamicClaimsPrincipalContributorOptions
{
public string AuthenticationScheme { get; set; }
public WebRemoteDynamicClaimsPrincipalContributorOptions()
{
AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientCommonModule.cs

@ -7,6 +7,7 @@ using Volo.Abp.Features;
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.Client; namespace Volo.Abp.AspNetCore.Mvc.Client;
@ -40,5 +41,12 @@ public class AbpAspNetCoreMvcClientCommonModule : AbpModule
context.Services.AddTransient<AbpApplicationConfigurationClientProxy>(); context.Services.AddTransient<AbpApplicationConfigurationClientProxy>();
context.Services.AddTransient<AbpTenantClientProxy>(); context.Services.AddTransient<AbpTenantClientProxy>();
var abpClaimsPrincipalFactoryOptions = context.Services.ExecutePreConfiguredActions<AbpClaimsPrincipalFactoryOptions>();
if (abpClaimsPrincipalFactoryOptions.IsRemoteRefreshEnabled)
{
context.Services.AddTransient<RemoteDynamicClaimsPrincipalContributor>();
context.Services.AddTransient<RemoteDynamicClaimsPrincipalContributorCache>();
}
} }
} }

46
framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributor.cs

@ -1,50 +1,10 @@
using System; using Volo.Abp.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Mvc.Client; namespace Volo.Abp.AspNetCore.Mvc.Client;
public class RemoteDynamicClaimsPrincipalContributor : AbpDynamicClaimsPrincipalContributorBase [DisableConventionalRegistration]
public class RemoteDynamicClaimsPrincipalContributor : RemoteDynamicClaimsPrincipalContributorBase<RemoteDynamicClaimsPrincipalContributor, RemoteDynamicClaimsPrincipalContributorCache>
{ {
public async override Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
if (identity == null)
{
return;
}
var userId = identity.FindUserId();
if (userId == null)
{
return;
}
var dynamicClaimsCache = context.GetRequiredService<RemoteDynamicClaimsPrincipalContributorCache>();
AbpDynamicClaimCacheItem dynamicClaims;
try
{
dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId());
}
catch (Exception e)
{
// In case if failed refresh remote dynamic cache, We force to clear the claims principal.
context.ClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
var logger = context.GetRequiredService<ILogger<RemoteDynamicClaimsPrincipalContributor>>();
logger.LogWarning(e, $"Failed to refresh remote dynamic claims cache for user: {userId.Value}");
return;
}
if (dynamicClaims.Claims.IsNullOrEmpty())
{
return;
}
await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims);
}
} }

31
framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs

@ -3,24 +3,20 @@ using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.Authentication; using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Mvc.Client; namespace Volo.Abp.AspNetCore.Mvc.Client;
public class RemoteDynamicClaimsPrincipalContributorCache : ITransientDependency public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsPrincipalContributorCacheBase<RemoteDynamicClaimsPrincipalContributorCache>
{ {
public const string HttpClientName = nameof(RemoteDynamicClaimsPrincipalContributorCache); public const string HttpClientName = nameof(RemoteDynamicClaimsPrincipalContributorCache);
public ILogger<RemoteDynamicClaimsPrincipalContributorCache> Logger { get; set; }
protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; } protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; }
protected IHttpClientFactory HttpClientFactory { get; } protected IHttpClientFactory HttpClientFactory { get; }
protected IOptions<AbpClaimsPrincipalFactoryOptions> AbpClaimsPrincipalFactoryOptions { get; }
protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; }
public RemoteDynamicClaimsPrincipalContributorCache( public RemoteDynamicClaimsPrincipalContributorCache(
@ -28,25 +24,20 @@ public class RemoteDynamicClaimsPrincipalContributorCache : ITransientDependency
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions, IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions,
IRemoteServiceHttpClientAuthenticator httpClientAuthenticator) IRemoteServiceHttpClientAuthenticator httpClientAuthenticator)
: base(abpClaimsPrincipalFactoryOptions)
{ {
Cache = cache; Cache = cache;
HttpClientFactory = httpClientFactory; HttpClientFactory = httpClientFactory;
AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions;
HttpClientAuthenticator = httpClientAuthenticator; HttpClientAuthenticator = httpClientAuthenticator;
Logger = NullLogger<RemoteDynamicClaimsPrincipalContributorCache>.Instance;
} }
public virtual async Task<AbpDynamicClaimCacheItem> GetAsync(Guid userId, Guid? tenantId = null) protected async override Task<AbpDynamicClaimCacheItem?> GetCacheAsync(Guid userId, Guid? tenantId = null)
{ {
Logger.LogDebug($"Get dynamic claims cache for user: {userId}"); return await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
var dynamicClaims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); }
if (dynamicClaims != null && !dynamicClaims.Claims.IsNullOrEmpty())
{
return dynamicClaims;
}
Logger.LogDebug($"Refresh dynamic claims for user: {userId} from remote service."); protected async override Task RefreshAsync(Guid userId, Guid? tenantId = null)
{
try try
{ {
var client = HttpClientFactory.CreateClient(HttpClientName); var client = HttpClientFactory.CreateClient(HttpClientName);
@ -60,13 +51,5 @@ public class RemoteDynamicClaimsPrincipalContributorCache : ITransientDependency
Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}"); Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}");
throw; throw;
} }
dynamicClaims = await Cache.GetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
if (dynamicClaims == null)
{
throw new AbpException($"Failed to refresh remote claims for user: {userId}");
}
return dynamicClaims;
} }
} }

5
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimsPrincipalFactoryOptions.cs

@ -12,11 +12,13 @@ public class AbpClaimsPrincipalFactoryOptions
public List<string> DynamicClaims { get; } public List<string> DynamicClaims { get; }
public bool IsRemoteRefreshEnabled { get; set; }
public string RemoteRefreshUrl { get; set; } public string RemoteRefreshUrl { get; set; }
public Dictionary<string, List<string>> ClaimsMap { get; set; } public Dictionary<string, List<string>> ClaimsMap { get; set; }
public bool IsDynamicClaimsEnabled { get; set; } public bool IsDynamicClaimsEnabled { get; set; }
public AbpClaimsPrincipalFactoryOptions() public AbpClaimsPrincipalFactoryOptions()
{ {
@ -36,6 +38,7 @@ public class AbpClaimsPrincipalFactoryOptions
}; };
RemoteRefreshUrl = "/api/account/dynamic-claims/refresh"; RemoteRefreshUrl = "/api/account/dynamic-claims/refresh";
IsDynamicClaimsEnabled = true;
ClaimsMap = new Dictionary<string, List<string>>() ClaimsMap = new Dictionary<string, List<string>>()
{ {

51
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/RemoteDynamicClaimsPrincipalContributorBase.cs

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Security.Claims;
public abstract class RemoteDynamicClaimsPrincipalContributorBase<TContributor, TContributorCache> : AbpDynamicClaimsPrincipalContributorBase
where TContributor : class
where TContributorCache : RemoteDynamicClaimsPrincipalContributorCacheBase<TContributorCache>
{
public async override Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
if (identity == null)
{
return;
}
var userId = identity.FindUserId();
if (userId == null)
{
return;
}
var dynamicClaimsCache = context.GetRequiredService<TContributor>().As<TContributorCache>();
AbpDynamicClaimCacheItem dynamicClaims;
try
{
dynamicClaims = await dynamicClaimsCache.GetAsync(userId.Value, identity.FindTenantId());
}
catch (Exception e)
{
// In case if failed refresh remote dynamic cache, We force to clear the claims principal.
context.ClaimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
var logger = context.GetRequiredService<ILogger<TContributor>>();
logger.LogWarning(e, $"Failed to refresh remote dynamic claims cache for user: {userId.Value}");
return;
}
if (dynamicClaims.Claims.IsNullOrEmpty())
{
return;
}
await AddDynamicClaimsAsync(context, identity, dynamicClaims.Claims);
}
}

55
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/RemoteDynamicClaimsPrincipalContributorCacheBase.cs

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace Volo.Abp.Security.Claims;
public abstract class RemoteDynamicClaimsPrincipalContributorCacheBase<TContributorCache>
{
public ILogger<TContributorCache> Logger { get; set; }
protected IOptions<AbpClaimsPrincipalFactoryOptions> AbpClaimsPrincipalFactoryOptions { get; }
protected RemoteDynamicClaimsPrincipalContributorCacheBase(IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions)
{
AbpClaimsPrincipalFactoryOptions = abpClaimsPrincipalFactoryOptions;
Logger = NullLogger<TContributorCache>.Instance;
}
public async Task<AbpDynamicClaimCacheItem> GetAsync(Guid userId, Guid? tenantId = null)
{
Logger.LogDebug($"Get dynamic claims cache for user: {userId}");
var dynamicClaims = await GetCacheAsync(userId, tenantId);
if (dynamicClaims != null && !dynamicClaims.Claims.IsNullOrEmpty())
{
return dynamicClaims;
}
Logger.LogDebug($"Refresh dynamic claims for user: {userId} from remote service.");
try
{
await RefreshAsync(userId, tenantId);
}
catch (Exception e)
{
Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}");
throw;
}
dynamicClaims = await GetCacheAsync(userId, tenantId);
if (dynamicClaims == null)
{
throw new AbpException($"Failed to refresh remote claims for user: {userId}");
}
return dynamicClaims;
}
protected abstract Task<AbpDynamicClaimCacheItem?> GetCacheAsync(Guid userId, Guid? tenantId = null);
protected abstract Task RefreshAsync(Guid userId, Guid? tenantId = null);
}

8
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs

@ -25,6 +25,14 @@ public class AbpIdentityDomainModule : AbpModule
{ {
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsRemoteRefreshEnabled = false;
});
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.AddAutoMapperObjectMapper<AbpIdentityDomainModule>(); context.Services.AddAutoMapperObjectMapper<AbpIdentityDomainModule>();

1
nupkg/common.ps1

@ -95,6 +95,7 @@ $projects = (
# framework # framework
"framework/src/Volo.Abp.ApiVersioning.Abstractions", "framework/src/Volo.Abp.ApiVersioning.Abstractions",
"framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer", "framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer",
"framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer.DynamicClaims",
"framework/src/Volo.Abp.AspNetCore.Authentication.OAuth", "framework/src/Volo.Abp.AspNetCore.Authentication.OAuth",
"framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect", "framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect",
"framework/src/Volo.Abp.AspNetCore", "framework/src/Volo.Abp.AspNetCore",

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyProjectNameBlazorModule.cs

@ -232,7 +232,6 @@ public class MyProjectNameBlazorModule : AbpModule
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options => context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{ {
options.IsDynamicClaimsEnabled = true; options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl;
}); });
} }

1
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyProjectNameWebModule.cs

@ -214,7 +214,6 @@ public class MyProjectNameWebModule : AbpModule
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options => context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{ {
options.IsDynamicClaimsEnabled = true; options.IsDynamicClaimsEnabled = true;
options.RemoteRefreshUrl = configuration["AuthServer:Authority"] + options.RemoteRefreshUrl;
}); });
} }

Loading…
Cancel
Save