具有自定义声明的 Blazor Windows 身份验证

编程


嘿大家,

我不知道我这样做是否完全错误,但我想要实现的是使用 Windows 身份验证在我的 IIS 上托管 Blazor 服务器应用程序,并附加一个自定义声明,以防止每个人访问,但只能访问一小部分人。

到目前为止,我已经找到了下面粘贴的代码,事实是,它在调试时与我的视觉工作室一起工作。 那里一切都很好,并且按预期完成了工作,但是一旦我将其发布到 IIS 服务器,它就会限制我的访问。

如果有人能引导我正确的方向,我会非常高兴。

提前致谢!

我尝试过的:

授权用户的代码 (program.cs)

using System.Security.Claims;
using System.Security.Principal;
using ImsServerMonitor.Data;
using Microsoft.AspNetCore.Authentication.Negotiate;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
    .AddNegotiate(options =>
    {
        options.Events = new NegotiateEvents
        {
            OnAuthenticated = context =>
            {
                if (context.Principal.Identity is WindowsIdentity windowsIdentity)
                {
                    string loginName = windowsIdentity.Name;
                    
                    if (loginName.Contains("User1")
                        || loginName.Contains("User2")
                        || loginName.Contains("User3")
                        || loginName.Contains("User4"))
                    {
                        var claims = new List<Claim>
                        {
                            new Claim("CustomClaim", "Admin")
                        };

                        context.Principal.AddIdentity(new ClaimsIdentity(claims));
                    }
                }

                return Task.CompletedTask;
            }
        };
    });
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policyBuilder => policyBuilder.RequireClaim("CustomClaim", "Admin"));
    // By default, all incoming requests will be authorized according to the default policy.
    //options.FallbackPolicy = options.DefaultPolicy;
    options.FallbackPolicy = options.GetPolicy("AdminOnly");
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<MonitorEngine>();
builder.Services.AddDevExpressBlazor();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

此外,在 IIS ofc 上,我为此站点启用了 WindowsAuth,并验证了 Negotiate Provider 位于列表顶部。

但我真的不明白问题是什么,即使 ChatGpt 也让我失败……或者我没有提出正确的问题。

解决方案1

实际需要做什么……经过幸运的研究并检查你的调试三次后:

program.cs 需要指向具有自定义方案的自定义验证器->

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "CustomWindowsAuthentication";
}).AddScheme<CustomWindowsAuthenticationOptions, CustomWindowsAuthenticationHandler>("CustomWindowsAuthentication", null);

那么你确实需要一个额外的类来处理这些东西:

public class CustomWindowsAuthenticationHandler : AuthenticationHandler<CustomWindowsAuthenticationOptions>
    {
        public CustomWindowsAuthenticationHandler(
            IOptionsMonitor<CustomWindowsAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Context.User.Identity.IsAuthenticated || !(Context.User.Identity is WindowsIdentity windowsIdentity))
            {
                return AuthenticateResult.NoResult();
            }

            var loginName = windowsIdentity.Name;
            if (loginName.Contains("User1")
                || loginName.Contains("User2")
                || loginName.Contains("User3")
                || loginName.Contains("User4"))
            {
                var claims = new List<Claim>
                {
                    new Claim("CustomClaim", "Admin")
                };

                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);

                var ticket = new AuthenticationTicket(principal, Scheme.Name);
                return AuthenticateResult.Success(ticket);
            }

            return AuthenticateResult.Fail("Custom authentication failed.");
        }
    }

为了完整起见,“CustomWindowsAuthenticationOptions”因为您也需要它,尽管它是空的,因为我不需要任何超级特殊选项。

public class CustomWindowsAuthenticationOptions : AuthenticationSchemeOptions
    {
        
    }

解决方案4

感谢您提出这个问题和解决方案。 效果很好。 我使用 .NET 8,必须对以下代码进行 1 个小更改

var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);

Context.User.AddIdentity(identity);
var ticket = new AuthenticationTicket(Context.User, Scheme.Name);
return AuthenticateResult.Success(ticket);

否则您无法在代码中的其他位置访问用户名。
在下面的代码中,如果我们使用原始代码,_authMessage 将返回 null。

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? AuthenticationState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthenticationState is not null)
        {
            var authState = await AuthenticationState;
            var user = authState.User;

            if (user?.Identity is not null && user.Identity.IsAuthenticated)
            {
                _authMessage = user.Identity.Name;
            }
        }
    }

コメント

タイトルとURLをコピーしました