用于Windows身份验证的HTTPClient或WebClient

问题描述 投票:0回答:1

我正在构建一个内部应用程序,它将使用Web Api与前端应用程序进行通信,我还计划使用Windows身份验证。

现在我正在使用WebClient,这样可以正常模拟。

有没有人能够使用HTTPClient做到这一点而不阻塞和进行异步调用?在我看来,HTTPClient意味着在应用程序生命周期中实例化一次,也许这并不能让每个请求通过模拟进行唯一身份验证。

我已经尝试了下面的代码,但我认为“GetStringAsync”方法是创建新任务的方法,我不知道如何使用模拟帐户而不是应用程序池帐户。

WindowsIdentity impersonatedUser = WindowsIdentity.GetCurrent();
using (WindowsImpersonationContext ctx = impersonatedUser.Impersonate())
{
Test = RequestRouter.Client.GetStringAsync("ServiceURL").Result;
}
asp.net asp.net-web-api windows-authentication dotnet-httpclient
1个回答
1
投票

我们有类似的需求,我们有一个用户与之交互的Web应用程序,并且该Web应用程序代表用户对服务进行Web API调用。我们通过HTTP头来将用户名从Web应用程序传递给服务,而不是尝试模拟(这非常棘手)。该服务检查标头 - 如果它没有看到标题,则会抛出错误。我们的控制器有一些逻辑可注入,以便在需要时从头部获取用户名。

我们使用ASP.NET Core来提供服务,但同样的想法也适用于ASP.NET。

这是我们用来确保标题存在的中间件,如果是,则抓住并存储它:

public class RequireUsernameHeaderMiddleware
{
    private const string HEADER_NAME = "hps-username";
    private const string API_ROOT = "/api";
    private readonly RequestDelegate _next;

    public RequireUsernameHeaderMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, IUsernameSetter usernameSetter)
    {
        // We only want to require the header on our API, not on our Swagger pages
        var pathString = new PathString(API_ROOT);

        if (context.Request.Path.StartsWithSegments(pathString))
        {
            if (!context.Request.Headers.Keys.Contains(HEADER_NAME))
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                await context.Response.WriteAsync("Username is missing");
                return;
            }

            usernameSetter.Username = context.Request.Headers[HEADER_NAME];
        }

        await _next.Invoke(context);
    }
}

然后胶水:

public interface IUsernameSetter
{
    string Username { set; }
}

public class UsernameProvider : IUsernameProvider, IUsernameSetter
{
    public string Username { get; set; } = String.Empty;
}

//when configuring services in Startup
services.AddScoped<UsernameProvider>();
services.AddScoped<IUsernameProvider>(service => service.GetService<UsernameProvider>());
services.AddScoped<IUsernameSetter>(service => service.GetService<UsernameProvider>());

//This gets called before app.UseMvc() in Startup when configuring the IApplicationBuilder
app.UseMiddleware<RequireUsernameHeaderMiddleware>();

现在我们的控制器:

readonly IUsernameProvider _usernameProvider;

public ProjectController(IUsernameProvider usernameProvider)
{
    _usernameProvider = usernameProvider;
}

public IActionResult SomeAction()
{
    _usernameProvider.Username; //now we can grab the username!
}

这不仅消除了模仿的需要,还简化了控制器的测试。

这是我们在Web应用程序中使用的Flurl代码:

return await _apiOptions.Url.AppendPathSegment("Project")
                            .WithHeader("hps-username", username)
                            .GetJsonAsync<List<Project>>();
© www.soinside.com 2019 - 2024. All rights reserved.