مصادقة Blazor Windows مع المطالبات المخصصة

برمجة


مرحبا جميعا،

لا أعرف ما إذا كنت أفعل ذلك بشكل خاطئ تمامًا ولكن ما أحاول تحقيقه هو استضافة تطبيق Blazor Server على IIS الخاص بي باستخدام مصادقة Windows ومطالبة مخصصة إضافية لمنع الجميع من الوصول، ولكن فقط يد مليئة بالأشخاص.

لقد شقت طريقي حتى الآن إلى الكود الملصق أدناه، والأمر هو أنه يعمل مع الاستوديو المرئي الخاص بي أثناء تصحيح الأخطاء. كل شيء على ما يرام هناك ويقوم بالمهمة كما هو متوقع، ولكن بمجرد أن أنشرها على خادم 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 لهذا الموقع وتحققت من أن موفر التفاوض موجود في أعلى القائمة.

لكنني حقًا لا أفهم ما هي المشكلة، حتى أن ChatGpt خذلني… أو فشلت في طرح السؤال الصحيح.

الحل 1

ما يجب فعله فعليًا… بعد إجراء بحث محظوظ والتحقق من تصحيح الأخطاء ثلاث مرات:

يحتاج البرنامج.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 واضطررت إلى إجراء تغيير بسيط واحد في الكود أدناه من

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 يُرجع قيمة فارغة إذا استخدمنا الكود الأصلي.

@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をコピーしました