Chrome 浏览器未添加服务器请求添加的身份验证标头

cld4siwp  于 2023-03-10  发布在  Go
关注(0)|答案(2)|浏览(170)

我有一个ASP .NETCoreWebApi项目,它在IIS中运行,并且始终支持NTLM和Negotiate作为身份验证方法。我需要为一个特定的端点添加基本身份验证,因此我为它创建了一个中间件:

public class BasicAuthMiddleware
{
    private readonly RequestDelegate next;

    public BasicAuthMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task InvokeAsync(HttpContext context, IOptions<List<BasicCredential>> credentials)
    {
        if (context.Request.Path.Value.Contains("/basicauthendpoint"))
        {
            var authHeader = (string)context.Request.Headers["Authorization"];
            if (authHeader != null && authHeader.StartsWith("Basic "))
            {
                //Validate credentials here...
                await next(context);
                return;
            }

            context.Response.Headers["WWW-Authenticate"] = "Basic";
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        }
        else
        {
            await next(context);
        }
    }
}

这是Startup.cs中的中间件顺序:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.UseMiddleware<BasicAuthMiddleware>();

app.UseSwagger();
app.UseSwaggerUI(c =>
{
    //Swagger stuff here
});

//Check if a particular header is in the request
app.UseCustomAuthMiddleware();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

中间件第一次按预期调用,当没有Authorization标头时,它返回401。然后浏览器显示用户名/密码对话框,我输入凭据并单击ok重新提交。
第二次调用中间件时,仍然缺少Authorization标头。
我使用的解决方案与GitHub问题中所解释的类似,但由于浏览器没有发送Authorization,用户名/密码提示不断弹出。
这是浏览器的问题还是因为我设置了其他身份验证方法?

li9yvcax

li9yvcax1#

因为基本的身份验证很糟糕,所以.net核心没有内置的支持。构建它的“正确方法”是创建一个自定义的AuthenticationHandler。例如,你可以通过MS身份验证你的用户;

public class BasicAuthOptions : AuthenticationSchemeOptions 
{
    public string Realm { get; set; }
    public bool AllowInsecure { get; set; }
}

public class BasicAuthHandler : AuthenticationHandler<BasicAuthOptions>
{
    private readonly UserManager<User> userManager;
    private readonly IUserClaimsPrincipalFactory<User> claimsFactory;

    public const string BasicAuthorization = nameof(BasicAuthorization);

    public BasicAuthHandler(IOptionsMonitor<BasicAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, UserManager<User> userManager, IUserClaimsPrincipalFactory<User> claimsFactory) : base(options, logger, encoder, clock)
    {
        this.userManager = userManager;
        this.claimsFactory = claimsFactory;
    }

    private static readonly Encoding encoding = Encoding.GetEncoding("iso-8859-1");
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.TryGetValue("Authorization", out var header))
            return AuthenticateResult.NoResult();

        var auth = AuthenticationHeaderValue.Parse(header.First());
        if (!auth.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) || auth.Parameter == null)
            return AuthenticateResult.NoResult();

        var buff = new byte[auth.Parameter.Length];
        if (!Convert.TryFromBase64String(auth.Parameter, buff, out var len))
            return AuthenticateResult.Fail("Authorization code not formatted properly.");
            
        var credentials = encoding.GetString(buff.AsSpan().Slice(0, len));

        var separator = credentials.IndexOf(':');
        if (separator == -1)
            return AuthenticateResult.Fail("Authorization code not formatted properly.");

        var name = credentials.Substring(0, separator);
        var password = credentials.Substring(separator + 1);

        var user = await userManager.FindByNameAsync(name);
        if (user == null
            || (userManager.SupportsUserLockout && await userManager.IsLockedOutAsync(user))
            || !await userManager.CheckPasswordAsync(user, password))
            return AuthenticateResult.Fail("The username or password is incorrect.");

        var principal = await claimsFactory.CreateAsync(user);
        var ident = principal.Identities.First();
        ident.AddClaim(new Claim(JwtRegisteredClaimNames.Amr, "pwd"));

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

    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        if (!Request.IsHttps && !Options.AllowInsecure)
            Response.StatusCode = 421;
        else
        {
            Response.StatusCode = 401;
            if (!string.IsNullOrWhiteSpace(Options.Realm))
                Response.Headers[HeaderNames.WWWAuthenticate] = $"Basic realm=\"{Options.Realm}\"";
        }
        return Task.CompletedTask;
    }
}

然后,您可以使用授权策略来选择哪些端点可以通过基本身份验证进行访问。与其他身份验证方案并行。

0s7z1bwu

0s7z1bwu2#

我误解了你的问题。你与我们分享的代码似乎是正确的。正确的顺序应该如下:

app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseMiddleware<BasicAuthMiddleware>();

        app.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");

        app.Run();

在您的场景中,在访问/basicauthendpoint时添加头是无效的,这是预期的行为。

因为Web服务器端无法更改客户端发送请求的行为。

正确的方式

1.登录应用程序时,我们可以生成和管理Cookie。
1.访问/basicauthendpoint时,我们可以修改当前cookie,或者添加一些内容,以满足认证要求。

相关问题