ConcurrentUser düzeltmesi
This commit is contained in:
parent
f6bc8b8473
commit
8234b1de2a
4 changed files with 43 additions and 63 deletions
|
|
@ -54,12 +54,6 @@ public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDep
|
|||
.Where(t => t.GetIsActive())
|
||||
.ToList();
|
||||
|
||||
logger.LogDebug(
|
||||
"IdentitySession temizliği: host + {Count} aktif tenant işlenecek (eşik: {Cutoff:s}).",
|
||||
activeTenants.Count, cutoff);
|
||||
|
||||
var totalDeleted = 0;
|
||||
|
||||
// Host (null) + tüm aktif tenant'lar
|
||||
var scopes = Enumerable.Repeat<Guid?>(null, 1)
|
||||
.Concat(activeTenants.Select(t => (Guid?)t.Id));
|
||||
|
|
@ -76,29 +70,21 @@ public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDep
|
|||
foreach (var session in staleSessions)
|
||||
{
|
||||
await sessionRepo.DeleteAsync(session, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
totalDeleted += staleSessions.Count;
|
||||
|
||||
if (staleSessions.Count > 0)
|
||||
{
|
||||
logger.LogInformation(
|
||||
"IdentitySession temizliği: {Scope} için {Deleted} atıl oturum silindi.",
|
||||
tenantId.HasValue ? $"Tenant [{tenantId}]" : "Host", staleSessions.Count);
|
||||
"AbpSessions temizliği: TenantId:{TenantId}, UserId:{UserId}, SignedIn:{SignedIn}, Now:{Now}",
|
||||
tenantId,
|
||||
session.UserId,
|
||||
session.SignedIn,
|
||||
DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalDeleted > 0)
|
||||
{
|
||||
logger.LogInformation(
|
||||
"IdentitySession temizliği tamamlandı: toplam {Deleted} oturum silindi.",
|
||||
totalDeleted);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "IdentitySession temizliği sırasında hata oluştu.");
|
||||
logger.LogError(ex, "AbpSessions temizliği sırasında hata oluştu.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Volo.Abp.DependencyInjection;
|
|||
using Volo.Abp.Identity;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Security.Claims;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sozsoft.Platform.Identity;
|
||||
|
||||
|
|
@ -20,14 +21,6 @@ public class PlatformSessionRevocationHandler :
|
|||
IOpenIddictServerHandler<HandleRevocationRequestContext>,
|
||||
ITransientDependency
|
||||
{
|
||||
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
||||
= OpenIddictServerHandlerDescriptor
|
||||
.CreateBuilder<HandleRevocationRequestContext>()
|
||||
.UseScopedHandler<PlatformSessionRevocationHandler>()
|
||||
.SetOrder(int.MaxValue - 10)
|
||||
.SetType(OpenIddictServerHandlerType.Custom)
|
||||
.Build();
|
||||
|
||||
private readonly IIdentitySessionRepository sessionRepo;
|
||||
private readonly ICurrentTenant currentTenant;
|
||||
private readonly ILogger<PlatformSessionRevocationHandler> logger;
|
||||
|
|
@ -51,12 +44,7 @@ public class PlatformSessionRevocationHandler :
|
|||
if (string.IsNullOrEmpty(userId) || !Guid.TryParse(userId, out var userGuid)) return;
|
||||
|
||||
// Token'dan tenant ID'yi al.
|
||||
var tenantIdStr = context.Principal?.GetClaim(AbpClaimTypes.TenantId);
|
||||
Guid? tenantId = null;
|
||||
if (!string.IsNullOrWhiteSpace(tenantIdStr) && Guid.TryParse(tenantIdStr, out var parsedTenantId))
|
||||
{
|
||||
tenantId = parsedTenantId;
|
||||
}
|
||||
var tenantId = ParseTenantId(context.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)?.Value);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -67,13 +55,13 @@ public class PlatformSessionRevocationHandler :
|
|||
foreach (var session in sessions)
|
||||
{
|
||||
await sessionRepo.DeleteAsync(session);
|
||||
}
|
||||
|
||||
if (sessions.Count > 0)
|
||||
{
|
||||
logger.LogInformation(
|
||||
"Token revocation: {Count} IdentitySession silindi. UserId: {UserId}, TenantId: {TenantId}",
|
||||
sessions.Count, userGuid, tenantId);
|
||||
"AbpSessions temizliği: TenantId:{TenantId}, UserId:{UserId}, SignedIn:{SignedIn}, Now:{Now}",
|
||||
tenantId,
|
||||
session.UserId,
|
||||
session.SignedIn,
|
||||
DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +69,9 @@ public class PlatformSessionRevocationHandler :
|
|||
{
|
||||
// Session temizleme hatası revocation akışını bloklamamalı.
|
||||
logger.LogWarning(ex,
|
||||
"Token revocation sonrası IdentitySession temizliğinde hata. UserId: {UserId}", userId);
|
||||
"Token revocation sonrası AbpSessions temizliğinde hata. UserId: {UserId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
private static Guid? ParseTenantId(string value) => !string.IsNullOrWhiteSpace(value) && Guid.TryParse(value, out var id) ? id : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,12 +253,16 @@ Your login code: {twoFactorToken}";
|
|||
if (existingSessions.Any())
|
||||
{
|
||||
foreach (var session in existingSessions)
|
||||
{
|
||||
await identitySessionRepository.DeleteAsync(session);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!await platformSignInManager.CheckConcurrentLimitAsync(user))
|
||||
{
|
||||
throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
||||
}
|
||||
}
|
||||
|
||||
await InsertSessionAsync(user, request.ClientId);
|
||||
|
|
@ -267,7 +271,7 @@ Your login code: {twoFactorToken}";
|
|||
catch (UserFriendlyException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "IdentitySession yönetiminde hata oluştu. UserId: {UserId}", user.Id);
|
||||
Logger.LogWarning(ex, "AbpSessions yönetiminde hata oluştu. UserId: {UserId}", user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,50 +279,46 @@ Your login code: {twoFactorToken}";
|
|||
protected override async Task<IActionResult> HandleRefreshTokenAsync(OpenIddictRequest request)
|
||||
{
|
||||
var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||
|
||||
var tenantId = ParseTenantId(info.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)?.Value);
|
||||
var userId = info.Principal?.GetClaim(OpenIddictConstants.Claims.Subject);
|
||||
|
||||
IdentityUser refreshUser = null;
|
||||
var userIdStr = info.Principal?.GetClaim(OpenIddictConstants.Claims.Subject);
|
||||
if (!userIdStr.IsNullOrWhiteSpace() && Guid.TryParse(userIdStr, out var userId))
|
||||
|
||||
using (CurrentTenant.Change(tenantId))
|
||||
{
|
||||
using (CurrentTenant.Change(tenantId))
|
||||
if (!userId.IsNullOrWhiteSpace() && Guid.TryParse(userId, out var userGuid))
|
||||
{
|
||||
refreshUser = await UserManager.FindByIdAsync(userId.ToString());
|
||||
refreshUser = await UserManager.FindByIdAsync(userId);
|
||||
if (refreshUser != null)
|
||||
{
|
||||
// Concurrent login limiti refresh token için de geçerlidir; yeni oturum açılmadan önce eskiler temizlenir.
|
||||
if (!await platformSignInManager.CheckConcurrentLimitAsync(refreshUser))
|
||||
throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
||||
|
||||
var existingSessions = await identitySessionRepository.GetListAsync(userId: userId);
|
||||
var existingSessions = await identitySessionRepository.GetListAsync(userId: userGuid);
|
||||
foreach (var session in existingSessions)
|
||||
{
|
||||
await identitySessionRepository.DeleteAsync(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IActionResult result;
|
||||
using (CurrentTenant.Change(tenantId))
|
||||
{
|
||||
result = await base.HandleRefreshTokenAsync(request);
|
||||
}
|
||||
var result = await base.HandleRefreshTokenAsync(request);
|
||||
|
||||
if (result is Microsoft.AspNetCore.Mvc.SignInResult && refreshUser != null)
|
||||
{
|
||||
try
|
||||
if (result is Microsoft.AspNetCore.Mvc.SignInResult && refreshUser != null)
|
||||
{
|
||||
using (CurrentTenant.Change(refreshUser.TenantId))
|
||||
try
|
||||
{
|
||||
await InsertSessionAsync(refreshUser, request.ClientId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Refresh token sonrası IdentitySession oluşturulamadı. UserId: {UserId}", refreshUser.Id);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Refresh token sonrası IdentitySession oluşturulamadı. UserId: {UserId}", refreshUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InsertSessionAsync(IdentityUser user, string clientId)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using OpenIddict.Server;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
using static OpenIddict.Server.OpenIddictServerEvents;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Account.Web;
|
||||
using Volo.Abp.AspNetCore.Auditing;
|
||||
|
|
@ -85,6 +87,8 @@ public class PlatformHttpApiHostModule : AbpModule
|
|||
{
|
||||
server.SetAccessTokenLifetime(TimeSpan.FromMinutes(60));
|
||||
server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(90));
|
||||
server.AddEventHandler<HandleRevocationRequestContext>(builder =>
|
||||
builder.UseScopedHandler<PlatformSessionRevocationHandler>());
|
||||
});
|
||||
builder.AddValidation(options =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue