使用带 async/await 的身份验证过滤器时如何处理 null HttpContext.Current?
How to deal with null HttpContext.Current when using auth filters with async/await?
在我的控制器中的一个 API 中,我需要引用 HttpContext.Current。在我实施自定义身份验证过滤器之前,一切正常。过滤器使用带有 await 的异步方法。但是,在过滤器就位后,HttpContext.Current 在到达我的控制器时为空。
我假设这是因为我的控制器在它使用的不同线程上执行由于过滤器中的 async/await。
如果是这种情况,我应该如何访问控制器中的 'original' 上下文?
这有点令人失望,因为过滤器似乎是一个很好的可插入想法,但不幸的是它们有一些[严重]缺点。
示例伪代码:
public class TestAuthFilter : Attribute, IAuthenticationFilter
{
private static string validationClaim = "testClaim";
private tokenValidationUri;
public bool AllowMultiple { get { return false; } }
public TestAuthFilter(string tokenValidationUri)
{
this.tokenValidationUri = tokenValidationUri;
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
// We are not adding any authentication challenges to the response
return Task.FromResult(0);
}
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
if (authorization == null)
{
return;
}
if (authorization.Scheme != "Bearer")
{
return;
}
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing access token", request);
return;
}
string token = authorization.Parameter;
IPrincipal principal = await AuthenticateAsync(token, cancellationToken);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid access token", request);
}
else
{
context.Principal = principal;
}
}
public async Task<IPrincipal> AuthenticateAsync(string token, CancellationToken cancellationToken)
{
using (HttpClient client = new HttpClient())
{
try
{
HttpResponseMessage response = await client.GetAsync(tokenValidationUri + token, cancellationToken);
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
if (result != null)
{
if (response.IsSuccessStatusCode)
{
var success = await response.Content.ReadAsAsync<TokenValidationSuccess>();
if (success.audience.Equals(validationAudience, StringComparison.Ordinal))
{
TestPrincipal principal = new TestPrincipal([...]);
return principal;
}
}
else
{
var error = await response.Content.ReadAsAsync<TokenValidationError>();
}
}
}
}
catch (HttpRequestException e)
{
Debug.WriteLine("Exception: {0}", e.Message);
}
}
return null;
}
}
如果有任何建议,我将不胜感激。
谢谢。
WebAPI
AuthenticateAsync
上的 link WebAPI
:http://www.asp.net/web-api/overview/security/authentication-filters
应作为参数 HttpAuthenticationContext
,其中包含 ActionContext
和 HttpRequestMessage
请求的线程安全版本。
这是到达那里的继承链:
(HttpRequestMessage)System.Web.Http.Filters.HttpAuthenticationContext.ActionContext.ControllerContext.Request
解决方案是将 "targetFramework=4.5" 包含在 web.config 中。感谢 Stephen Cleary - 请参阅上面的评论。
在我的控制器中的一个 API 中,我需要引用 HttpContext.Current。在我实施自定义身份验证过滤器之前,一切正常。过滤器使用带有 await 的异步方法。但是,在过滤器就位后,HttpContext.Current 在到达我的控制器时为空。
我假设这是因为我的控制器在它使用的不同线程上执行由于过滤器中的 async/await。
如果是这种情况,我应该如何访问控制器中的 'original' 上下文?
这有点令人失望,因为过滤器似乎是一个很好的可插入想法,但不幸的是它们有一些[严重]缺点。
示例伪代码:
public class TestAuthFilter : Attribute, IAuthenticationFilter
{
private static string validationClaim = "testClaim";
private tokenValidationUri;
public bool AllowMultiple { get { return false; } }
public TestAuthFilter(string tokenValidationUri)
{
this.tokenValidationUri = tokenValidationUri;
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
// We are not adding any authentication challenges to the response
return Task.FromResult(0);
}
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
if (authorization == null)
{
return;
}
if (authorization.Scheme != "Bearer")
{
return;
}
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing access token", request);
return;
}
string token = authorization.Parameter;
IPrincipal principal = await AuthenticateAsync(token, cancellationToken);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid access token", request);
}
else
{
context.Principal = principal;
}
}
public async Task<IPrincipal> AuthenticateAsync(string token, CancellationToken cancellationToken)
{
using (HttpClient client = new HttpClient())
{
try
{
HttpResponseMessage response = await client.GetAsync(tokenValidationUri + token, cancellationToken);
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
if (result != null)
{
if (response.IsSuccessStatusCode)
{
var success = await response.Content.ReadAsAsync<TokenValidationSuccess>();
if (success.audience.Equals(validationAudience, StringComparison.Ordinal))
{
TestPrincipal principal = new TestPrincipal([...]);
return principal;
}
}
else
{
var error = await response.Content.ReadAsAsync<TokenValidationError>();
}
}
}
}
catch (HttpRequestException e)
{
Debug.WriteLine("Exception: {0}", e.Message);
}
}
return null;
}
}
如果有任何建议,我将不胜感激。
谢谢。
WebAPI
AuthenticateAsync
上的 link WebAPI
:http://www.asp.net/web-api/overview/security/authentication-filters
应作为参数 HttpAuthenticationContext
,其中包含 ActionContext
和 HttpRequestMessage
请求的线程安全版本。
这是到达那里的继承链:
(HttpRequestMessage)System.Web.Http.Filters.HttpAuthenticationContext.ActionContext.ControllerContext.Request
解决方案是将 "targetFramework=4.5" 包含在 web.config 中。感谢 Stephen Cleary - 请参阅上面的评论。