从 Web Api 2 IAuthenticationFilter AuthenticateAsync 方法设置 cookie
Set cookie from Web Api 2 IAuthenticationFilter AuthenticateAsync method
使用 Web Api 2.2,我有一个自定义 IAuthenticationFilter
,我使用自定义方案对客户端请求进行身份验证。
基本上,当客户端未通过身份验证并想要访问受保护的资源时,他会在请求的同时发送 Authorization
header: Authorization: MyCustomScheme XXXXXXX
。然后过滤器验证凭据,对用户进行身份验证并生成无状态身份验证令牌以供进一步访问(类似于 JWT)。
我想将生成的身份验证令牌存储在 cookie 中。当出现在传入请求中时,cookie 在单独的过滤器(此处未显示)中进行本地验证。
我的问题是,如果我尝试这样设置 cookie:
Task IAuthenticationFilter.AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
if (context.Request.Headers.Authorization != null &&
string.Equals(context.Request.Headers.Authorization.Scheme, "MyCustomScheme", StringComparison.OrdinalIgnoreCase))
{
// This works
CustomPrincipal principal = this.ValidateCredentials(context.Request.Headers.Authorization.Parameter);
context.Principal = principal;
// This doesn't work: context.ActionContext.Response is null
var cookie = new CookieHeaderValue("MySessionCookie", principal.AuthenticationToken) { Path = "/", HttpOnly = true };
context.ActionContext.Response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
return Task.FromResult(0);
}
然后失败,因为 context.ActionContext.Response
为空。如何将 cookie 添加到 AuthenticateAsync
中的响应?
查看相关内容:
(你可以在评论中看到人们遇到同样的问题)。
您可能需要实施 IRequiresSessionState 才能使 cookie 持久保存?
参见:http://www.strathweb.com/2012/11/adding-session-support-to-asp-net-web-api/
除了 IAuthenticationFilter
之外,我还通过实施 IActionFilter
让过滤器正常工作。此方法有效,因为您可以在同一位置访问请求、响应和用户身份。这是我的实现:
async Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
// Process the request pipeline and get the response (this causes the action to be executed)
HttpResponseMessage response = await continuation();
// If the user is authenticated and the token is not present in the request cookies, then it needs to be set
CustomPrincipal principal = actionContext.ControllerContext.RequestContext.Principal as CustomPrincipal;
if (principal != null && !actionContext.Request.Headers.GetCookies("MySessionCookie").Any())
{
// Set the cookie in the response
var cookie = new CookieHeaderValue("MySessionCookie", principal.AuthenticationToken) { Path = "/", HttpOnly = true };
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
return response;
}
我发现这种方法非常不切实际(混合接口),您绝对应该可以访问 IAuthenticationFilter.AuthenticateAsync
中的响应(例如通过异步延续回调,或者能够访问操作结果(IHttpActionResult
) 在上下文中,就像在同一接口的 ChallengeAsync
方法中一样)。
我的要求是添加一个 header 但添加 cookie 应该很容易适应。
我对此采取了不同的方法。我把我想加的header放到了context.Request.Properties
里面。然后在 ChallengeAsync
中(无论每个请求都会调用它)通过 IHttpActionResult
我检查 属性 是否存在,如果存在则将其添加到 header 中。像这样:
protected class AddRenewOnAauthorizedResult : IHttpActionResult {
public const string RenewalPropertyKey = "ETicket.RenewalKey";
public AddRenewOnAauthorizedResult(HttpRequestMessage request, IHttpActionResult innerResult) {
this.Request = request;
this.InnerResult = innerResult;
}
public HttpRequestMessage Request { get; set; }
public IHttpActionResult InnerResult { get; set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
HttpResponseMessage response = await this.InnerResult.ExecuteAsync(cancellationToken);
if (Request.Properties.ContainsKey(RenewalPropertyKey)) Request.response.Headers.Add("X-ETicket-Renew", Request.Properties(RenewalPropertyKey));
Return response;
}
}
然后在 ChallengeAsync
:
public Threading.Tasks.Task ChallengeAsync(HttpAuthenticationChallengeContext context, Threading.CancellationToken cancellationToken)
{
context.Result = new AddRenewOnAauthorizedResult(context.Request, context.Result);
return Task.FromResult(0);
}
使用 Web Api 2.2,我有一个自定义 IAuthenticationFilter
,我使用自定义方案对客户端请求进行身份验证。
基本上,当客户端未通过身份验证并想要访问受保护的资源时,他会在请求的同时发送 Authorization
header: Authorization: MyCustomScheme XXXXXXX
。然后过滤器验证凭据,对用户进行身份验证并生成无状态身份验证令牌以供进一步访问(类似于 JWT)。
我想将生成的身份验证令牌存储在 cookie 中。当出现在传入请求中时,cookie 在单独的过滤器(此处未显示)中进行本地验证。
我的问题是,如果我尝试这样设置 cookie:
Task IAuthenticationFilter.AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
if (context.Request.Headers.Authorization != null &&
string.Equals(context.Request.Headers.Authorization.Scheme, "MyCustomScheme", StringComparison.OrdinalIgnoreCase))
{
// This works
CustomPrincipal principal = this.ValidateCredentials(context.Request.Headers.Authorization.Parameter);
context.Principal = principal;
// This doesn't work: context.ActionContext.Response is null
var cookie = new CookieHeaderValue("MySessionCookie", principal.AuthenticationToken) { Path = "/", HttpOnly = true };
context.ActionContext.Response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
return Task.FromResult(0);
}
然后失败,因为 context.ActionContext.Response
为空。如何将 cookie 添加到 AuthenticateAsync
中的响应?
查看相关内容:
您可能需要实施 IRequiresSessionState 才能使 cookie 持久保存?
参见:http://www.strathweb.com/2012/11/adding-session-support-to-asp-net-web-api/
除了 IAuthenticationFilter
之外,我还通过实施 IActionFilter
让过滤器正常工作。此方法有效,因为您可以在同一位置访问请求、响应和用户身份。这是我的实现:
async Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
// Process the request pipeline and get the response (this causes the action to be executed)
HttpResponseMessage response = await continuation();
// If the user is authenticated and the token is not present in the request cookies, then it needs to be set
CustomPrincipal principal = actionContext.ControllerContext.RequestContext.Principal as CustomPrincipal;
if (principal != null && !actionContext.Request.Headers.GetCookies("MySessionCookie").Any())
{
// Set the cookie in the response
var cookie = new CookieHeaderValue("MySessionCookie", principal.AuthenticationToken) { Path = "/", HttpOnly = true };
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
}
return response;
}
我发现这种方法非常不切实际(混合接口),您绝对应该可以访问 IAuthenticationFilter.AuthenticateAsync
中的响应(例如通过异步延续回调,或者能够访问操作结果(IHttpActionResult
) 在上下文中,就像在同一接口的 ChallengeAsync
方法中一样)。
我的要求是添加一个 header 但添加 cookie 应该很容易适应。
我对此采取了不同的方法。我把我想加的header放到了context.Request.Properties
里面。然后在 ChallengeAsync
中(无论每个请求都会调用它)通过 IHttpActionResult
我检查 属性 是否存在,如果存在则将其添加到 header 中。像这样:
protected class AddRenewOnAauthorizedResult : IHttpActionResult {
public const string RenewalPropertyKey = "ETicket.RenewalKey";
public AddRenewOnAauthorizedResult(HttpRequestMessage request, IHttpActionResult innerResult) {
this.Request = request;
this.InnerResult = innerResult;
}
public HttpRequestMessage Request { get; set; }
public IHttpActionResult InnerResult { get; set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
HttpResponseMessage response = await this.InnerResult.ExecuteAsync(cancellationToken);
if (Request.Properties.ContainsKey(RenewalPropertyKey)) Request.response.Headers.Add("X-ETicket-Renew", Request.Properties(RenewalPropertyKey));
Return response;
}
}
然后在 ChallengeAsync
:
public Threading.Tasks.Task ChallengeAsync(HttpAuthenticationChallengeContext context, Threading.CancellationToken cancellationToken)
{
context.Result = new AddRenewOnAauthorizedResult(context.Request, context.Result);
return Task.FromResult(0);
}