问题描述
我创建了此类用于从请求中获取Header值.
公共类AuthenticationHeader{私有静态IHttpContextAccessor _httpContextAccessor;公共AuthenticationHeader(IHttpContextAccessor httpContextAccessor){_httpContextAccessor = httpContextAccessor;}公共字符串AuthHeader =>_httpContextAccessor.HttpContext?.Request.Headers ["Authorization"];}
我已经在我的startup.cs中注册了它
services.AddSingleton< AuthenticationHeader>();
并将其注入到我的其他此类班级中.
公共BaseClient(HttpClient客户端,ILogger< BaseClient>记录器,AuthenticationHeader authHeader){_client =客户;client.BaseAddress = new Uri("yrl");client.DefaultRequestHeaders.Add("Accept","application/json");_logger =记录器;AuthHeader = authHeader;}
现在,因为我已将其注册为 Singleton
.因此,当第一次调用我的Api并在标头中提供Authorization值时,会成功调用api,但问题是当我传递空的Authorization标头时,它仍会成功调用api,因为由于Singleton而存储了旧标头值.我怎样才能解决这个问题?还有其他方法可以做我正在做的事情.
尝试使用 HttpClientFactory
(已添加Asp.Net Core 2.1)和 HttpMessageHandler
来实现你想做什么.
您可以在 ConfigureServices
方法
public void ConfigureServices(IServiceCollection服务){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
,您可以为注册的 HttpClient
配置 HttpMessageHandler
.消息处理程序的代码很简单,如下所示:
公共类AuthHeaderHandler:DelegatingHandler{受保护的覆盖异步任务< HttpResponseMessage>SendAsync(HttpRequestMessage请求,CancellationToken取消令牌){如果(!request.Headers.Contains("Authorization")){返回新的HttpResponseMessage(HttpStatusCode.Forbidden){内容=新的StringContent(不存在授权标头")};}return await base.SendAsync(request,cancelToken);}}
为了注册上述处理程序,您的代码将如下所示:
public void ConfigureServices(IServiceCollection服务){services.AddTransient< AuthHeaderHandler>();services.AddHttpClient< BaseClient>(client =>{//为简洁起见,省略了代码...}).AddHttpMessageHandler< AuthHeaderHandler>();}
如果需要,您可以在消息处理程序中注入所需的任何内容.但是,无需在 BaseClient
中注入IHttpContextAccessor.要阅读有关HttpClientFactory和HttpMessageHandlers的更多信息,请参见此链接和此.我希望这会有所帮助.
更新后的答案
请查看使用IHttpContextAccessor并修改HttpRequestMessage的HttpMessageHandler的更具体示例,即在进行调用之前添加Authorization标头.您可以根据需要修改逻辑.
公共类AuthHeaderHandler:DelegatingHandler{私有只读HttpContext _httpContext;公共AuthHeaderHandler(IHttpContextAccessor contextAccessor){_httpContext = contextAccessor.HttpContext;}受保护的覆盖异步任务< HttpResponseMessage>SendAsync(HttpRequestMessage请求,CancellationToken取消令牌){如果(_httpContext!= null){var accessToken =等待_httpContext.GetTokenAsync(TokenKeys.Access);如果(!string.IsNullOrEmpty(accessToken)){//使用新的Authorization令牌修改请求标头request.Headers.Authorization = new AuthenticationHeaderValue("Bearer",accessToken);}}return await base.SendAsync(request,cancelToken);}}
更新的答案2
请查看我拥有的简单解决方案上传到GitHub.该解决方案甚至比我最初建议的还要简单.由于您没有集成任何基于身份的身份验证/授权,因此可以简单地使用CustomActionFilter(我将其称为 ValidateAuthHeader
)来检查AuthHeader是否存在,如果不存在,则返回通常的403./p>
在 ValidateAuthHeader
中,我利用了您之前发布的中间件代码.然后,您可以简单地在需要此检查的ActionMethods或Controllers上添加此属性.
请查看 DataController
和 ValuesController
. DataController
将收到类型化的 HttpClient
,该类型将用于调用 values
端点. ValidateAuthHeader
存在于 GetValues
上,并将检查AuthHeader.如果不存在,将产生错误.
[Route("api/[controller]")][ApiController]公共类DataController:ControllerBase{私有只读MyHttpClient _client;公共DataController(MyHttpClient客户端){_client =客户;}[ValidateAuthHeader]公共异步Task< IActionResult>GetValues(){var response =等待_client.GetAsync("api/values");var contents =等待响应.Content.ReadAsStringAsync();返回新的ContentResult{内容=内容,ContentType ="application/json",状态码= 200};}}
其余流程与我最初建议的相同.呼叫将通过 AuthHeaderHandler
传递,该AuthHeaderHandler 是已注册的 MyHttpClient
的 HttpMessageHandler
.请查看 Startup.cs
.
处理程序将通过 HttpContextAccessor
检索 HttpContext
,并将检查 AuthHeader
.如果存在,它将添加到RequestMessage参数中.
我希望这会有所帮助.随时问您可能有的任何问题.
不使用HttpMessageHandler设置身份验证标头
修改MyHttpClient并添加一个名为 SetAuthHeader
公共类MyHttpClient{私有只读HttpClient _httpClient;公共MyHttpClient(HttpClient客户端){_httpClient =客户端;}公共无效SetAuthHeader(字符串值){_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",value);}}
然后在您的操作方法中调用此方法,因为届时您将在HttpContext.Request中拥有AuthHeader
[ValidateAuthHeader]公共异步Task< IActionResult>GetValues(){var authHeader = Request.Headers ["Authorization"];_client.SetAuthHeader(authHeader.First());var response =等待_client.GetAsync("api/values");var contents =等待响应.Content.ReadAsStringAsync();返回新的ContentResult{内容=内容,ContentType ="application/json",状态码= 200};}
删除AuthHeaderHandler注册并删除AuthHeaderHandler.
I have created this class for getting the Header value from requests.
public class AuthenticationHeader
{
private static IHttpContextAccessor _httpContextAccessor;
public AuthenticationHeader(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string AuthHeader => _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
}
and that I have registered that in my startup.cs like this
services.AddSingleton<AuthenticationHeader>();
And its been injected into my other classes like this.
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;
}
Now as I have registered that as Singleton
. So when call my Api for first time and provide the Authorization value in header the api is called successfully but the issue is when i pass empty Authorization header it still call's api successfully as it is storing old header value due to Singleton. How can I fix this? Is there any otherways to do what I am doing.
Try using HttpClientFactory
, that was added Asp.Net Core 2.1, in conjunction with HttpMessageHandler
to achieve what you are trying to do.
You can register the HttpClient in ConfigureServices
method
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");
});
}
With the above code in place, your BaseClient
will receive the HttpClient instance via DI.
In order to validate/inspect the AuthHeader
you can configure the HttpMessageHandler
for the registered HttpClient
. The code for the message handler is simple like below:
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);
}
}
In order to register the above handler, your code will look like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<AuthHeaderHandler>();
services.AddHttpClient<BaseClient>(client =>
{
//code omitted for brevity
...
})
.AddHttpMessageHandler<AuthHeaderHandler>();
}
You can inject whatever you need inside the message handler if needed. However, no need to inject the IHttpContextAccessor in the BaseClient
. To read more about HttpClientFactory and HttpMessageHandlers please see this link and this. I hope this helps.
UPDATED ANSWER
Please have a look at the more concrete example of HttpMessageHandler that uses the IHttpContextAccessor and modifies the HttpRequestMessage i.e. adds the Authorization header before the call is made. You can modify the logic as per your need.
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);
}
}
UPDATED ANSWER 2
Please have a look at the simple solution that I have uploaded to GitHub. The solution is even simpler than I originally suggested. As you are not integrating any identity-based Authentication/Authorization, you can simply use a CustomActionFilter, I called it ValidateAuthHeader
, to check if the AuthHeader is present or not and return the usual 403 if absent.
Within the ValidateAuthHeader
, I have utilised the middleware code that you posted earlier. You can then simply add this attribute on the ActionMethods or Controllers which require this check.
Please have a look at the DataController
and ValuesController
. The DataController
will receive the typed HttpClient
that will be used to call the values
endpoint. ValidateAuthHeader
is present on the GetValues
and will check for the AuthHeader. If it's absent it will generate the error.
[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
};
}
}
The rest of the flow is the same as I originally suggested. The call will be passed through the AuthHeaderHandler
which is an HttpMessageHandler
for the registered MyHttpClient
. Please have a look at the Startup.cs
.
The handler will retrieve the HttpContext
via HttpContextAccessor
and will check for the AuthHeader
. If present, it will add it to the RequestMessage parameter.
I hope this helps. Feel free to ask any questions that you may have.
Setting Auth Header without using HttpMessageHandler
Modify the MyHttpClient and add a public method called 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);
}
}
Then call this method in your action method as you will have the AuthHeader in the HttpContext.Request at that point
[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
};
}
Remove the AuthHeaderHandler registration and delete the AuthHeaderHandler.
这篇关于HttpContext标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!