From 187ede5cd2d2b67110809509136f4913e2819f0c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 16 Jun 2021 14:47:37 +0800 Subject: [PATCH] Add custom authorization scheme support for dashbaord. #906 --- build/version.props | 2 +- .../MyDashboardAuthenticationHandler.cs | 50 +++++++++++++++++++ samples/Sample.Dashboard.Auth/Startup.cs | 15 +++--- .../CAP.DashboardOptions.cs | 17 ++++++- src/DotNetCore.CAP.Dashboard/UiMiddleware.cs | 11 ++++ 5 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs diff --git a/build/version.props b/build/version.props index 536a49b..030cf21 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 5 1 - 0 + 1 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs b/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs new file mode 100644 index 0000000..c4320b6 --- /dev/null +++ b/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs @@ -0,0 +1,50 @@ +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Sample.Dashboard.Auth +{ + public class MyDashboardAuthenticationSchemeOptions : AuthenticationSchemeOptions + { + + } + + public class MyDashboardAuthenticationHandler : AuthenticationHandler + { + public MyDashboardAuthenticationHandler(IOptionsMonitor options, + ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + options.CurrentValue.ForwardChallenge = ""; + } + + protected override Task HandleAuthenticateAsync() + { + var testAuthHeaderPresent = Request.Headers["X-Base-Token"].Contains("xxx"); + + var authResult = testAuthHeaderPresent ? AuthenticatedTestUser() : AuthenticateResult.NoResult(); + + return Task.FromResult(authResult); + } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + { + Response.Headers["WWW-Authenticate"] = "MyDashboardScheme"; + + return base.HandleChallengeAsync(properties); + } + + private AuthenticateResult AuthenticatedTestUser() + { + var claims = new[] { new Claim(ClaimTypes.Name, "My Dashboard user") }; + var identity = new ClaimsIdentity(claims, "MyDashboardScheme"); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, "MyDashboardScheme"); + + return AuthenticateResult.Success(ticket); + } + } +} diff --git a/samples/Sample.Dashboard.Auth/Startup.cs b/samples/Sample.Dashboard.Auth/Startup.cs index e0335ae..0c2b00f 100644 --- a/samples/Sample.Dashboard.Auth/Startup.cs +++ b/samples/Sample.Dashboard.Auth/Startup.cs @@ -21,7 +21,7 @@ namespace Sample.Dashboard.Auth .AddAuthorization() .AddAuthentication(options => { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() @@ -36,7 +36,8 @@ namespace Sample.Dashboard.Auth options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); - }); + }) + .AddScheme("MyDashboardScheme",null); services.AddCors(x => { @@ -45,13 +46,15 @@ namespace Sample.Dashboard.Auth p.WithOrigins("http://localhost:8080").AllowCredentials().AllowAnyHeader().AllowAnyMethod(); }); }); - + services.AddCap(cap => { cap.UseDashboard(d => { d.UseChallengeOnAuth = true; d.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + d.UseAuth = true; + d.DefaultAuthenticationScheme = "MyDashboardScheme"; }); cap.UseMySql(_configuration.GetValue("ConnectionString")); cap.UseRabbitMQ(aa => @@ -76,16 +79,16 @@ namespace Sample.Dashboard.Auth public void Configure(IApplicationBuilder app) { - app.UseAuthentication(); app.UseCors(); app.UseRouting(); + app.UseAuthentication(); app.UseAuthorization(); app.UseCookiePolicy(); app.UseEndpoints(endpoints => { - endpoints.MapControllers(); + endpoints.MapControllers(); }); } - } + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs index 57daf1b..a9af5ae 100644 --- a/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs +++ b/src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs @@ -13,16 +13,29 @@ namespace DotNetCore.CAP } public string PathMatch { get; set; } - + /// /// The interval the /stats endpoint should be polled with. /// public int StatsPollingInterval { get; set; } + /// + /// Enable authentication on dashboard request. + /// + public bool UseAuth { get; set; } + + /// + /// Default scheme used for authentication. If no scheme is set, the DefaultScheme set up in AddAuthentication will be used. + /// + public string DefaultAuthenticationScheme { get; set; } + + /// + /// Enable authentication challenge on dashboard request. + /// public bool UseChallengeOnAuth { get; set; } /// - /// Default ChallengeScheme used for Dashboard authentication. If no scheme is set, the DefaultScheme set up in AddAuthentication will be used. + /// Default scheme used for authentication challenge. If no scheme is set, the DefaultChallengeScheme set up in AddAuthentication will be used. /// public string DefaultChallengeScheme { get; set; } } diff --git a/src/DotNetCore.CAP.Dashboard/UiMiddleware.cs b/src/DotNetCore.CAP.Dashboard/UiMiddleware.cs index c8ab361..ead672f 100644 --- a/src/DotNetCore.CAP.Dashboard/UiMiddleware.cs +++ b/src/DotNetCore.CAP.Dashboard/UiMiddleware.cs @@ -47,11 +47,22 @@ namespace DotNetCore.CAP.Dashboard if (httpMethod == "GET" && Regex.IsMatch(path, $"^/?{Regex.Escape(_options.PathMatch)}/?index.html$", RegexOptions.IgnoreCase)) { + if (_options.UseAuth) + { + var result = await httpContext.AuthenticateAsync(_options.DefaultAuthenticationScheme); + + if (result.Succeeded && result.Principal != null) + { + httpContext.User = result.Principal; + } + } + var isAuthenticated = httpContext.User?.Identity?.IsAuthenticated; if (isAuthenticated == false && _options.UseChallengeOnAuth) { await httpContext.ChallengeAsync(_options.DefaultChallengeScheme); + return; }