Browse Source

fix(identity): Lock when handling user sessions

- Lock when cleaning up user sessions
- Lock when refreshing the user session
pull/1273/head
colin 1 year ago
parent
commit
06f9e82665
  1. 71
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionCacheItemSynchronizer.cs
  2. 28
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionCleanupBackgroundWorker.cs

71
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionCacheItemSynchronizer.cs

@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
@ -22,15 +23,18 @@ public class IdentitySessionCacheItemSynchronizer :
{ {
public ILogger<IdentitySessionCacheItemSynchronizer> Logger { protected get; set; } public ILogger<IdentitySessionCacheItemSynchronizer> Logger { protected get; set; }
protected ISettingProvider SettingProvider { get; } protected ISettingProvider SettingProvider { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected IIdentitySessionCache IdentitySessionCache { get; } protected IIdentitySessionCache IdentitySessionCache { get; }
protected IIdentitySessionStore IdentitySessionStore { get; } protected IIdentitySessionStore IdentitySessionStore { get; }
public IdentitySessionCacheItemSynchronizer( public IdentitySessionCacheItemSynchronizer(
ISettingProvider settingProvider, ISettingProvider settingProvider,
IAbpDistributedLock distributedLock,
IIdentitySessionCache identitySessionCache, IIdentitySessionCache identitySessionCache,
IIdentitySessionStore identitySessionStore) IIdentitySessionStore identitySessionStore)
{ {
SettingProvider = settingProvider; SettingProvider = settingProvider;
DistributedLock = distributedLock;
IdentitySessionCache = identitySessionCache; IdentitySessionCache = identitySessionCache;
IdentitySessionStore = identitySessionStore; IdentitySessionStore = identitySessionStore;
@ -45,34 +49,71 @@ public class IdentitySessionCacheItemSynchronizer :
[UnitOfWork] [UnitOfWork]
public async virtual Task HandleEventAsync(EntityCreatedEto<IdentitySessionEto> eventData) public async virtual Task HandleEventAsync(EntityCreatedEto<IdentitySessionEto> eventData)
{ {
await RefreshSessionCache(eventData.Entity); var lockKey = $"{nameof(IdentitySessionCacheItemSynchronizer)}_{nameof(EntityCreatedEto<IdentitySessionEto>)}";
await CheckConcurrentLoginStrategy(eventData.Entity); await using (var handle = await DistributedLock.TryAcquireAsync(lockKey))
{
Logger.LogInformation($"Lock is acquired for {lockKey}");
if (handle == null)
{
Logger.LogInformation($"Handle is null because of the locking for : {lockKey}");
return;
}
await RefreshSessionCache(eventData.Entity);
await CheckConcurrentLoginStrategy(eventData.Entity);
}
} }
[UnitOfWork]
public async virtual Task HandleEventAsync(IdentitySessionChangeAccessedEvent eventData) public async virtual Task HandleEventAsync(IdentitySessionChangeAccessedEvent eventData)
{ {
var idetitySession = await IdentitySessionStore.FindAsync(eventData.SessionId); var lockKey = $"{nameof(IdentitySessionCacheItemSynchronizer)}_{nameof(IdentitySessionChangeAccessedEvent)}";
if (idetitySession != null) await using (var handle = await DistributedLock.TryAcquireAsync(lockKey))
{ {
if (!eventData.IpAddresses.IsNullOrWhiteSpace()) Logger.LogInformation($"Lock is acquired for {lockKey}");
if (handle == null)
{ {
idetitySession.SetIpAddresses(eventData.IpAddresses.Split(",")); Logger.LogInformation($"Handle is null because of the locking for : {lockKey}");
return;
} }
idetitySession.UpdateLastAccessedTime(eventData.LastAccessed);
await IdentitySessionStore.UpdateAsync(idetitySession); var idetitySession = await IdentitySessionStore.FindAsync(eventData.SessionId);
} if (idetitySession != null)
else {
{ if (!eventData.IpAddresses.IsNullOrWhiteSpace())
// 数据库中不存在会话, 清理缓存, 后续请求会话失效 {
await IdentitySessionCache.RemoveAsync(eventData.SessionId); idetitySession.SetIpAddresses(eventData.IpAddresses.Split(","));
}
idetitySession.UpdateLastAccessedTime(eventData.LastAccessed);
await IdentitySessionStore.UpdateAsync(idetitySession);
}
else
{
// 数据库中不存在会话, 清理缓存, 后续请求会话失效
await IdentitySessionCache.RemoveAsync(eventData.SessionId);
}
} }
} }
public async virtual Task HandleEventAsync(EntityDeletedEventData<IdentityUser> eventData) public async virtual Task HandleEventAsync(EntityDeletedEventData<IdentityUser> eventData)
{ {
// 用户被删除, 移除所有会话 var lockKey = $"{nameof(IdentitySessionCacheItemSynchronizer)}_{nameof(EntityDeletedEventData<IdentityUser>)}";
await IdentitySessionStore.RevokeAllAsync(eventData.Entity.Id); await using (var handle = await DistributedLock.TryAcquireAsync(lockKey))
{
Logger.LogInformation($"Lock is acquired for {lockKey}");
if (handle == null)
{
Logger.LogInformation($"Handle is null because of the locking for : {lockKey}");
return;
}
// 用户被删除, 移除所有会话
await IdentitySessionStore.RevokeAllAsync(eventData.Entity.Id);
}
} }
protected async virtual Task RefreshSessionCache(IdentitySessionEto session) protected async virtual Task RefreshSessionCache(IdentitySessionEto session)

28
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/Session/IdentitySessionCleanupBackgroundWorker.cs

@ -1,20 +1,25 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.BackgroundWorkers; using Volo.Abp.BackgroundWorkers;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace LINGYUN.Abp.Identity.Session; namespace LINGYUN.Abp.Identity.Session;
public class IdentitySessionCleanupBackgroundWorker : AsyncPeriodicBackgroundWorkerBase public class IdentitySessionCleanupBackgroundWorker : AsyncPeriodicBackgroundWorkerBase
{ {
protected IAbpDistributedLock DistributedLock { get; }
protected IdentitySessionCleanupOptions Options { get; } protected IdentitySessionCleanupOptions Options { get; }
public IdentitySessionCleanupBackgroundWorker( public IdentitySessionCleanupBackgroundWorker(
AbpAsyncTimer timer, AbpAsyncTimer timer,
IServiceScopeFactory serviceScopeFactory, IServiceScopeFactory serviceScopeFactory,
IOptions<IdentitySessionCleanupOptions> options) IOptions<IdentitySessionCleanupOptions> options,
IAbpDistributedLock distributedLock)
: base(timer, serviceScopeFactory) : base(timer, serviceScopeFactory)
{ {
DistributedLock = distributedLock;
Options = options.Value; Options = options.Value;
timer.Period = Options.CleanupPeriod; timer.Period = Options.CleanupPeriod;
} }
@ -26,9 +31,22 @@ public class IdentitySessionCleanupBackgroundWorker : AsyncPeriodicBackgroundWor
return; return;
} }
await workerContext await using (var handle = await DistributedLock.TryAcquireAsync(nameof(IdentitySessionCleanupBackgroundWorker)))
.ServiceProvider {
.GetRequiredService<IdentitySessionCleanupService>() Logger.LogInformation($"Lock is acquired for {nameof(IdentitySessionCleanupBackgroundWorker)}");
.CleanAsync();
if (handle != null)
{
await workerContext
.ServiceProvider
.GetRequiredService<IdentitySessionCleanupService>()
.CleanAsync();
Logger.LogInformation($"Lock is released for {nameof(IdentitySessionCleanupBackgroundWorker)}");
return;
}
Logger.LogInformation($"Handle is null because of the locking for : {nameof(IdentitySessionCleanupBackgroundWorker)}");
}
} }
} }

Loading…
Cancel
Save