using LINGYUN.Abp.Identity; using LINGYUN.Abp.Identity.Session; using LINGYUN.Abp.Identity.Settings; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.DistributedLocking; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Settings; using Volo.Abp.Uow; using Volo.Abp.Users; namespace LY.MicroService.AuthServer.Handlers; /// /// 会话控制事件处理器 /// public class IdentitySessionAccessEventHandler : IDistributedEventHandler, IDistributedEventHandler>, IDistributedEventHandler>, ITransientDependency { public ILogger Logger { protected get; set; } protected ISettingProvider SettingProvider { get; } protected IAbpDistributedLock DistributedLock { get; } protected IIdentitySessionCache IdentitySessionCache { get; } protected IIdentitySessionStore IdentitySessionStore { get; } public IdentitySessionAccessEventHandler( ISettingProvider settingProvider, IAbpDistributedLock distributedLock, IIdentitySessionCache identitySessionCache, IIdentitySessionStore identitySessionStore) { SettingProvider = settingProvider; DistributedLock = distributedLock; IdentitySessionCache = identitySessionCache; IdentitySessionStore = identitySessionStore; Logger = NullLogger.Instance; } [UnitOfWork] public async virtual Task HandleEventAsync(EntityCreatedEto eventData) { // 新会话创建时检查登录策略 var lockKey = $"{nameof(IdentitySessionAccessEventHandler)}_{nameof(EntityCreatedEto)}"; 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 CheckConcurrentLoginStrategy(eventData.Entity); } } [UnitOfWork] public async virtual Task HandleEventAsync(EntityDeletedEto eventData) { // 用户被删除, 移除所有会话 var lockKey = $"{nameof(IdentitySessionAccessEventHandler)}_{nameof(EntityDeletedEto)}"; 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); } } [UnitOfWork] public async virtual Task HandleEventAsync(IdentitySessionChangeAccessedEvent eventData) { // 会话访问更新 var lockKey = $"{nameof(IdentitySessionAccessEventHandler)}_{nameof(IdentitySessionChangeAccessedEvent)}"; 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; } var idetitySession = await IdentitySessionStore.FindAsync(eventData.SessionId); if (idetitySession != null) { if (!eventData.IpAddresses.IsNullOrWhiteSpace()) { idetitySession.SetIpAddresses(eventData.IpAddresses.Split(",")); } idetitySession.UpdateLastAccessedTime(eventData.LastAccessed); await IdentitySessionStore.UpdateAsync(idetitySession); } else { // 数据库中不存在会话, 清理缓存, 后续请求会话失效 await IdentitySessionCache.RemoveAsync(eventData.SessionId); } } } protected async virtual Task CheckConcurrentLoginStrategy(IdentitySessionEto session) { // 创建一个会话后根据策略使其他会话失效 var strategySet = await SettingProvider.GetOrNullAsync(IdentitySettingNames.Session.ConcurrentLoginStrategy); Logger.LogDebug($"The concurrent login strategy is: {strategySet}"); if (!strategySet.IsNullOrWhiteSpace() && Enum.TryParse(strategySet, true, out var strategy)) { switch (strategy) { // 限制用户相同设备 case ConcurrentLoginStrategy.LogoutFromSameTypeDevicesLimit: var sameTypeDevicesCountSet = await SettingProvider.GetAsync(IdentitySettingNames.Session.LogoutFromSameTypeDevicesLimit, 1); Logger.LogDebug($"Clear other sessions on the device {session.Device} and save only {sameTypeDevicesCountSet} sessions."); await IdentitySessionStore.RevokeWithAsync( session.UserId, session.Device, session.Id, sameTypeDevicesCountSet); break; // 限制登录设备 case ConcurrentLoginStrategy.LogoutFromSameTypeDevices: Logger.LogDebug($"Clear all other sessions on the device {session.Device}."); await IdentitySessionStore.RevokeAllAsync( session.UserId, session.Device, session.Id); break; // 限制多端登录 case ConcurrentLoginStrategy.LogoutFromAllDevices: Logger.LogDebug($"Clear all other user sessions."); await IdentitySessionStore.RevokeAllAsync( session.UserId, session.Id); break; } } } }