From 8a5762fbb7a29c51a86a1c4ba1beab698f7465e5 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 17 Apr 2024 15:44:54 +0800 Subject: [PATCH] Add `AbpAuthenticationSessionState` to logout from other tabs --- .../Bundling/BlazorGlobalScriptContributor.cs | 1 + .../Web/AbpAuthenticationOptions.cs | 8 +++ .../Components/Web/ILocalStorageService.cs | 10 +++ .../Components/Web/LocalStorageService.cs | 31 +++++++++ .../Security/AbpAuthenticationSessionState.cs | 65 +++++++++++++++++++ .../authentication-session-state-listener.js | 27 ++++++++ .../ComponentsComponentsBundleContributor.cs | 2 + ...bpAspNetCoreComponentsWebAssemblyModule.cs | 10 +++ .../ClientProxyExceptionEventHandler.cs | 7 +- 9 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAuthenticationOptions.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ILocalStorageService.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/LocalStorageService.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpAuthenticationSessionState.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/authentication-session-state-listener.js diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs index d99dc6284d..a98223e5cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server.Theming/Bundling/BlazorGlobalScriptContributor.cs @@ -15,5 +15,6 @@ public class BlazorGlobalScriptContributor : BundleContributor context.Files.AddIfNotContains("/_framework/blazor.server.js"); } context.Files.AddIfNotContains("/_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); + context.Files.AddIfNotContains("/_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/authentication-session-state-listener.js"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAuthenticationOptions.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAuthenticationOptions.cs new file mode 100644 index 0000000000..e1a06c72b1 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/AbpAuthenticationOptions.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.Components.Web; + +public class AbpAuthenticationOptions +{ + public string LoginUrl { get; set; } = "Account/Login"; + + public string LogoutUrl { get; set; } = "Account/Logout"; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ILocalStorageService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ILocalStorageService.cs new file mode 100644 index 0000000000..f22e392732 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/ILocalStorageService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.AspNetCore.Components.Web; + +public interface ILocalStorageService +{ + public ValueTask SetItemAsync(string key, string value); + public ValueTask GetItemAsync(string key); + public ValueTask RemoveItemAsync(string key); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/LocalStorageService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/LocalStorageService.cs new file mode 100644 index 0000000000..d49e85ec5f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/LocalStorageService.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Microsoft.JSInterop; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Components.Web; + +[Dependency(ReplaceServices = true)] +public class LocalStorageService : ILocalStorageService, ITransientDependency +{ + public IJSRuntime JsRuntime { get; } + + public LocalStorageService(IJSRuntime jsRuntime) + { + JsRuntime = jsRuntime; + } + + public async ValueTask SetItemAsync(string key, string value) + { + await JsRuntime.InvokeVoidAsync("localStorage.setItem", key, value); + } + + public async ValueTask GetItemAsync(string key) + { + return await JsRuntime.InvokeAsync("localStorage.getItem", key); + } + + public async ValueTask RemoveItemAsync(string key) + { + await JsRuntime.InvokeVoidAsync("localStorage.removeItem", key); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpAuthenticationSessionState.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpAuthenticationSessionState.cs new file mode 100644 index 0000000000..eb7b2ded47 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpAuthenticationSessionState.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.Extensions.Options; +using Volo.Abp.Users; + +namespace Volo.Abp.AspNetCore.Components.Web.Security; + +public class AbpAuthenticationSessionState : ComponentBase +{ + private const string SessionKey = "authentication-session-id"; + + [Inject] + protected ILocalStorageService LocalStorage { get; set; } = default!; + + [Inject] + protected ICurrentUser CurrentUser { get; set; } = default!; + + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; + + [Inject] + protected IOptions AuthenticationOptions { get; set; } = default!; + + protected async override Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + NavigationManager.RegisterLocationChangingHandler(OnLocationChangingAsync); + if (CurrentUser.IsAuthenticated) + { + await SetAuthenticationStateAsync(); + } + else + { + await ClearAuthenticationStateAsync(); + } + } + } + + protected virtual async ValueTask OnLocationChangingAsync(LocationChangingContext context) + { + if (context.TargetLocation.Contains(AuthenticationOptions.Value.LogoutUrl)) + { + await ClearAuthenticationStateAsync(); + } + } + + protected virtual async Task SetAuthenticationStateAsync() + { + var sessionId = await LocalStorage.GetItemAsync(SessionKey); + if (sessionId.IsNullOrWhiteSpace()) + { + sessionId = CurrentUser.FindSessionId() ?? Guid.NewGuid().ToString(); + } + + await LocalStorage.SetItemAsync(SessionKey, sessionId); + } + + protected virtual async Task ClearAuthenticationStateAsync() + { + await LocalStorage.RemoveItemAsync(SessionKey); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/authentication-session-state-listener.js b/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/authentication-session-state-listener.js new file mode 100644 index 0000000000..a7e80991f6 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/wwwroot/libs/abp/js/authentication-session-state-listener.js @@ -0,0 +1,27 @@ +(function () { + + var sessionKey = 'authentication-session-id'; + + window.addEventListener('storage', function (event) { + + console.log(event); + if (event.key !== sessionKey) { + return; + } + + var previousSessionId = event.oldValue + var sessionId = event.newValue; + + if(previousSessionId === sessionId) { + return; + } + + if(previousSessionId || !sessionId) { + abp.utils.removeOidcUser(); + window.location.reload(); + return; + } + + location.assign('/') + }); +}()); \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs index 942edd473c..35f77cdc31 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming/ComponentsComponentsBundleContributor.cs @@ -9,6 +9,8 @@ public class ComponentsComponentsBundleContributor : IBundleContributor context.Add("_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"); context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); + context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); + context.Add("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/authentication-session-state-listener.js"); } public void AddStyles(BundleContext context) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs index 268d6a2dc7..25a57616e4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Volo.Abp.AspNetCore.Components.Server; using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.AspNetCore.Components.Web.ExceptionHandling; using Volo.Abp.AspNetCore.Components.Web.Security; @@ -49,6 +50,15 @@ public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule context.Services .GetHostBuilder().Logging .AddProvider(new AbpExceptionHandlingLoggerProvider(context.Services)); + + if (!context.Services.ExecutePreConfiguredActions().IsBlazorWebApp) + { + Configure(options => + { + options.LoginUrl = "authentication/login"; + options.LogoutUrl = "authentication/logout"; + }); + } } public override void PostConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs index 7a5646ebae..b3614064f9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/ClientProxyExceptionEventHandler.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.JSInterop; using Volo.Abp.AspNetCore.Components.Server; +using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; using Volo.Abp.Http; @@ -28,21 +29,23 @@ public class ClientProxyExceptionEventHandler : ILocalEventHandler>(); + if (!options.Value.IsBlazorWebApp) { var navigationManager = scope.ServiceProvider.GetRequiredService(); var accessTokenProvider = scope.ServiceProvider.GetRequiredService(); + var authenticationOptions = scope.ServiceProvider.GetRequiredService>(); var result = await accessTokenProvider.RequestAccessToken(); if (result.Status != AccessTokenResultStatus.Success) { - navigationManager.NavigateToLogout("authentication/logout"); + navigationManager.NavigateToLogout(authenticationOptions.Value.LogoutUrl); return; } result.TryGetToken(out var token); if (token != null && DateTimeOffset.Now >= token.Expires.AddMinutes(-5)) { - navigationManager.NavigateToLogout("authentication/logout"); + navigationManager.NavigateToLogout(authenticationOptions.Value.LogoutUrl); } } else