我创建了这个类来从请求中获取Header值。
public class AuthenticationHeader
{
private static IHttpContextAccessor _httpContextAccessor;
public AuthenticationHeader(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string AuthHeader => _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
}
并且我已经在我的startup.cs中注册了这样的内容
services.AddSingleton<AuthenticationHeader>();
它被注入我的其他课程。
public BaseClient(HttpClient client, ILogger<BaseClient> logger, AuthenticationHeader authHeader)
{
_client = client;
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
_logger = logger;
AuthHeader = authHeader;
}
现在我已将其注册为Singleton
。因此,当第一次调用我的Api并在头文件中提供Authorization值时,api被成功调用,但问题是当我传递空的Authorization头时,它仍然成功调用了api,因为它存储了由于Singleton而导致的旧头值。我怎样才能解决这个问题?还有其他方法可以做我正在做的事情。
尝试使用添加了Asp.Net Core 2.1的HttpClientFactory
和HttpMessageHandler
来实现您的目标。
您可以在ConfigureServices
方法中注册HttpClient
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<BaseClient>(client =>
{
client.BaseAddress = new Uri("yrl");
client.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
}
有了上面的代码,你的BaseClient
将通过DI接收HttpClient实例。
为了验证/检查AuthHeader
,您可以为注册的HttpMessageHandler
配置HttpClient
。消息处理程序的代码很简单,如下所示:
public class AuthHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("Authorization"))
{
return new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("No Authorization header is present")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
为了注册上面的处理程序,您的代码如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<AuthHeaderHandler>();
services.AddHttpClient<BaseClient>(client =>
{
//code omitted for brevity
...
})
.AddHttpMessageHandler<AuthHeaderHandler>();
}
如果需要,您可以在消息处理程序中注入所需的任何内容。但是,无需在BaseClient
中注入IHttpContextAccessor。要了解有关HttpClientFactory和HttpMessageHandlers的更多信息,请参阅此link和this。我希望这有帮助。
更新的答案
请查看使用IHttpContextAccessor的HttpMessageHandler的更具体的示例,并修改HttpRequestMessage,即在调用之前添加Authorization标头。您可以根据需要修改逻辑。
public class AuthHeaderHandler : DelegatingHandler
{
private readonly HttpContext _httpContext;
public AuthHeaderHandler(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (_httpContext != null)
{
var accessToken = await _httpContext.GetTokenAsync(TokenKeys.Access);
if (!string.IsNullOrEmpty(accessToken))
{
// modify the request header with the new Authorization token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}
return await base.SendAsync(request, cancellationToken);
}
}
更新的答案2
请看一下我上传到GitHub的simple solution。解决方案比我最初建议的更简单。由于您没有集成任何基于身份的身份验证/授权,您可以简单地使用CustomActionFilter(我称之为ValidateAuthHeader
)来检查AuthHeader是否存在,如果不存在则返回通常的403。
在ValidateAuthHeader
中,我使用了您之前发布的中间件代码。然后,您只需在需要此检查的ActionMethods或Controllers上添加此属性。
请看看DataController
和ValuesController
。 DataController
将收到用于调用HttpClient
端点的类型values
。 ValidateAuthHeader
出现在GetValues
上,并将检查AuthHeader。如果它不存在则会产生错误。
[Route("api/[controller]")]
[ApiController]
public class DataController : ControllerBase
{
private readonly MyHttpClient _client;
public DataController(MyHttpClient client)
{
_client = client;
}
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
}
其余的流程与我最初建议的相同。这个电话将通过AuthHeaderHandler
传递,这是注册的HttpMessageHandler
的MyHttpClient
。请看看Startup.cs
。
处理程序将通过HttpContext
检索HttpContextAccessor
并检查AuthHeader
。如果存在,它将把它添加到RequestMessage参数。
我希望这有帮助。随意提出您可能遇到的任何问题。
在不使用HttpMessageHandler的情况下设置Auth Header
修改MyHttpClient并添加一个名为SetAuthHeader
的公共方法
public class MyHttpClient
{
private readonly HttpClient _httpClient;
public MyHttpClient(HttpClient client)
{
_httpClient = client;
}
public void SetAuthHeader(string value)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", value);
}
}
然后在您的操作方法中调用此方法,因为此时您将在HttpContext.Request中具有AuthHeader
[ValidateAuthHeader]
public async Task<IActionResult> GetValues()
{
var authHeader = Request.Headers["Authorization"];
_client.SetAuthHeader(authHeader.First());
var response = await _client.GetAsync("api/values");
var contents = await response.Content.ReadAsStringAsync();
return new ContentResult
{
Content = contents,
ContentType = "application/json",
StatusCode = 200
};
}
删除AuthHeaderHandler注册并删除AuthHeaderHandler。