Windows 身份验证的 HTTPClient 或 WebClient
HTTPClient or WebClient for Windows Authentication
我正在构建一个将使用 Web Api 与前端应用程序通信的内部应用程序,我还计划使用 Windows 身份验证。
我现在使用的是 WebClient,它可以很好地模拟模拟。
有没有人能够在不阻塞和进行异步调用的情况下使用 HTTPClient 做到这一点?在我看来,HTTPClient 应该在应用程序生命周期中实例化一次,也许这并不是让每个请求通过模拟具有唯一身份验证的结论。
我试过下面的代码,但我认为 "GetStringAsync" 方法是创建新任务的方法,我不知道如何使用模拟帐户而不是应用程序池帐号。
WindowsIdentity impersonatedUser = WindowsIdentity.GetCurrent();
using (WindowsImpersonationContext ctx = impersonatedUser.Impersonate())
{
Test = RequestRouter.Client.GetStringAsync("ServiceURL").Result;
}
我们有一个类似的需求,我们有一个用户与之交互的 Web 应用程序,并且该 Web 应用程序代表用户对服务进行 Web API 调用。我们没有尝试模拟(这非常棘手),而是通过 HTTP header 将用户名从 Web 应用程序传递到服务来绕过它。该服务检查 header - 如果它没有看到 header 它会抛出错误。我们有一些逻辑可注入我们的控制器,以便在需要时从 header 中获取用户名。
我们的服务使用了 ASP.NET 核心,但同样的想法也适用于 ASP.NET。
这是我们用来确保 header 存在的中间件,如果存在,则获取并存储它:
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!
}
这不仅消除了模拟的需要,还简化了控制器的测试。
这是我们在网络应用程序中使用的 Flurl 代码:
return await _apiOptions.Url.AppendPathSegment("Project")
.WithHeader("hps-username", username)
.GetJsonAsync<List<Project>>();
我正在构建一个将使用 Web Api 与前端应用程序通信的内部应用程序,我还计划使用 Windows 身份验证。
我现在使用的是 WebClient,它可以很好地模拟模拟。
有没有人能够在不阻塞和进行异步调用的情况下使用 HTTPClient 做到这一点?在我看来,HTTPClient 应该在应用程序生命周期中实例化一次,也许这并不是让每个请求通过模拟具有唯一身份验证的结论。
我试过下面的代码,但我认为 "GetStringAsync" 方法是创建新任务的方法,我不知道如何使用模拟帐户而不是应用程序池帐号。
WindowsIdentity impersonatedUser = WindowsIdentity.GetCurrent();
using (WindowsImpersonationContext ctx = impersonatedUser.Impersonate())
{
Test = RequestRouter.Client.GetStringAsync("ServiceURL").Result;
}
我们有一个类似的需求,我们有一个用户与之交互的 Web 应用程序,并且该 Web 应用程序代表用户对服务进行 Web API 调用。我们没有尝试模拟(这非常棘手),而是通过 HTTP header 将用户名从 Web 应用程序传递到服务来绕过它。该服务检查 header - 如果它没有看到 header 它会抛出错误。我们有一些逻辑可注入我们的控制器,以便在需要时从 header 中获取用户名。
我们的服务使用了 ASP.NET 核心,但同样的想法也适用于 ASP.NET。
这是我们用来确保 header 存在的中间件,如果存在,则获取并存储它:
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!
}
这不仅消除了模拟的需要,还简化了控制器的测试。
这是我们在网络应用程序中使用的 Flurl 代码:
return await _apiOptions.Url.AppendPathSegment("Project")
.WithHeader("hps-username", username)
.GetJsonAsync<List<Project>>();