Browse Source

Merge branch 'master' of https://github.com/dotnetcore/CAP

master
Savorboard 3 years ago
parent
commit
37c430c1a7
6 changed files with 144 additions and 30 deletions
  1. +1
    -1
      build/version.props
  2. +50
    -0
      samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs
  3. +9
    -6
      samples/Sample.Dashboard.Auth/Startup.cs
  4. +57
    -15
      src/DotNetCore.CAP.Dashboard/CAP.BuilderExtension.cs
  5. +24
    -2
      src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs
  6. +3
    -6
      src/DotNetCore.CAP.Dashboard/UiMiddleware.cs

+ 1
- 1
build/version.props View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<VersionMajor>5</VersionMajor>
<VersionMinor>1</VersionMinor>
<VersionPatch>0</VersionPatch>
<VersionPatch>1</VersionPatch>
<VersionQuality></VersionQuality>
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
</PropertyGroup>


+ 50
- 0
samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs View File

@@ -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<MyDashboardAuthenticationSchemeOptions>
{
public MyDashboardAuthenticationHandler(IOptionsMonitor<MyDashboardAuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
options.CurrentValue.ForwardChallenge = "";
}

protected override Task<AuthenticateResult> 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);
}
}
}

+ 9
- 6
samples/Sample.Dashboard.Auth/Startup.cs View File

@@ -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<MyDashboardAuthenticationSchemeOptions, MyDashboardAuthenticationHandler>("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<string>("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();
});
}
}
}

}

+ 57
- 15
src/DotNetCore.CAP.Dashboard/CAP.BuilderExtension.cs View File

@@ -3,9 +3,11 @@

using System;
using System.Reflection;
using System.Threading.Tasks;
using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.Dashboard.GatewayProxy;
using DotNetCore.CAP.Dashboard.NodeDiscovery;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -29,8 +31,8 @@ namespace DotNetCore.CAP

var provider = app.ApplicationServices;

var option = provider.GetService<DashboardOptions>();
if (option != null)
var options = provider.GetService<DashboardOptions>();
if (options != null)
{
if (provider.GetService<DiscoveryOptions>() != null)
{
@@ -39,7 +41,7 @@ namespace DotNetCore.CAP

app.UseMiddleware<UiMiddleware>();

app.Map(option.PathMatch + "/api", false, x =>
app.Map(options.PathMatch + "/api", false, x =>
{

var builder = new RouteBuilder(x);
@@ -55,18 +57,24 @@ namespace DotNetCore.CAP
{

builder.MapGet(getAttr.Template, async (request, response, data) =>
{
var actionProvider = new RouteActionProvider(request, response, data);
try
{
await executor.ExecuteAsync(actionProvider, null);
}
catch (Exception ex)
{
response.StatusCode = StatusCodes.Status500InternalServerError;
await response.WriteAsync(ex.Message);
}
});
{
if (!await Authentication(request.HttpContext, options))
{
response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}

var actionProvider = new RouteActionProvider(request, response, data);
try
{
await executor.ExecuteAsync(actionProvider, null);
}
catch (Exception ex)
{
response.StatusCode = StatusCodes.Status500InternalServerError;
await response.WriteAsync(ex.Message);
}
});
}

var postAttr = method.GetCustomAttribute<HttpPostAttribute>();
@@ -74,6 +82,12 @@ namespace DotNetCore.CAP
{
builder.MapPost(postAttr.Template, async (request, response, data) =>
{
if (!await Authentication(request.HttpContext, options))
{
response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}

var actionProvider = new RouteActionProvider(request, response, data);
try
{
@@ -97,6 +111,34 @@ namespace DotNetCore.CAP
return app;
}

internal static async Task<bool> Authentication(HttpContext context, DashboardOptions options)
{
if (options.UseAuth)
{
var result = await context.AuthenticateAsync(options.DefaultAuthenticationScheme);

if (result.Succeeded && result.Principal != null)
{
context.User = result.Principal;
}
else
{
return false;
}
}

var isAuthenticated = context.User?.Identity?.IsAuthenticated;

if (isAuthenticated == false && options.UseChallengeOnAuth)
{
await context.ChallengeAsync(options.DefaultChallengeScheme);

return false;
}

return true;
}

private static void CheckRequirement(IApplicationBuilder app)
{
var marker = app.ApplicationServices.GetService<CapMarkerService>();


+ 24
- 2
src/DotNetCore.CAP.Dashboard/CAP.DashboardOptions.cs View File

@@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

// ReSharper disable once CheckNamespace

namespace DotNetCore.CAP
{
public class DashboardOptions
@@ -12,17 +13,38 @@ namespace DotNetCore.CAP
StatsPollingInterval = 2000;
}

/// <summary>
/// When behind the proxy, specify the base path to allow spa call prefix.
/// </summary>
public string PathBase { get; set; }

/// <summary>
/// Path prefix to match from url path.
/// </summary>
public string PathMatch { get; set; }
/// <summary>
/// The interval the /stats endpoint should be polled with.
/// </summary>
public int StatsPollingInterval { get; set; }

/// <summary>
/// Enable authentication on dashboard request.
/// </summary>
public bool UseAuth { get; set; }

/// <summary>
/// Default scheme used for authentication. If no scheme is set, the DefaultScheme set up in AddAuthentication will be used.
/// </summary>
public string DefaultAuthenticationScheme { get; set; }

/// <summary>
/// Enable authentication challenge on dashboard request.
/// </summary>
public bool UseChallengeOnAuth { get; set; }

/// <summary>
/// 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.
/// </summary>
public string DefaultChallengeScheme { get; set; }
}

+ 3
- 6
src/DotNetCore.CAP.Dashboard/UiMiddleware.cs View File

@@ -5,7 +5,6 @@ using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@@ -47,11 +46,9 @@ namespace DotNetCore.CAP.Dashboard

if (httpMethod == "GET" && Regex.IsMatch(path, $"^/?{Regex.Escape(_options.PathMatch)}/?index.html$", RegexOptions.IgnoreCase))
{
var isAuthenticated = httpContext.User?.Identity?.IsAuthenticated;

if (isAuthenticated == false && _options.UseChallengeOnAuth)
if (!await CapBuilderExtension.Authentication(httpContext, _options))
{
await httpContext.ChallengeAsync(_options.DefaultChallengeScheme);
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}

@@ -63,7 +60,7 @@ namespace DotNetCore.CAP.Dashboard

using var sr = new StreamReader(stream);
var htmlBuilder = new StringBuilder(await sr.ReadToEndAsync());
htmlBuilder.Replace("%(servicePrefix)", _options.PathMatch + "/api");
htmlBuilder.Replace("%(servicePrefix)", _options.PathBase + _options.PathMatch + "/api");
htmlBuilder.Replace("%(pollingInterval)", _options.StatsPollingInterval.ToString());
await httpContext.Response.WriteAsync(htmlBuilder.ToString(), Encoding.UTF8);



Loading…
Cancel
Save