C# 从Web Api 2 IAAuthenticationFilter AuthenticationAsync方法设置cookie

C# 从Web Api 2 IAAuthenticationFilter AuthenticationAsync方法设置cookie,c#,authentication,cookies,asp.net-web-api2,C#,Authentication,Cookies,Asp.net Web Api2,使用WebAPI2.2,我有一个自定义的iaAuthenticationFilter,用于使用自定义方案对客户端请求进行身份验证 基本上,当客户机未经过身份验证并且想要访问受保护的资源时,他会在请求旁边发送一个授权头:授权:MyCustomScheme XXXXXXX。然后,过滤器验证凭据,对用户进行身份验证,并生成无状态身份验证令牌以供进一步访问(类似于 我想将生成的身份验证令牌存储在cookie中。当存在于传入请求中时,cookie将在单独的筛选器中进行本地验证(此处不提供) 我的问题是,如

使用WebAPI2.2,我有一个自定义的
iaAuthenticationFilter
,用于使用自定义方案对客户端请求进行身份验证

基本上,当客户机未经过身份验证并且想要访问受保护的资源时,他会在请求旁边发送一个
授权
头:
授权:MyCustomScheme XXXXXXX
。然后,过滤器验证凭据,对用户进行身份验证,并生成无状态身份验证令牌以供进一步访问(类似于

我想将生成的身份验证令牌存储在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);
}
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;

}
然后它失败,因为
context.ActionContext.Response
为空。如何从
AuthenticateAsync
中向响应添加cookie

见相关文件:
(您可以在评论中看到,人们遇到了相同的问题)。

您可能需要实现iRequiresessionState才能持久保存cookie


请参阅:

除了
iaauthenticationfilter
之外,我还通过实现
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;
}
异步任务IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext,CancellationToken CancellationToken,Func continuation) { //处理请求管道并获取响应(这会导致执行操作) HttpResponseMessage响应=等待继续(); //如果用户已通过身份验证,且请求cookie中不存在令牌,则需要对其进行设置 CustomPrincipal principal=actionContext.ControllerContext.RequestContext.principal作为CustomPrincipal; if(principal!=null&&!actionContext.Request.Headers.GetCookies(“MySessionCookie”).Any()) { //在响应中设置cookie var cookie=new CookieHeaderValue(“MySessionCookie”,principal.AuthenticationToken){Path=“/”,HttpOnly=true}; AddCookies(新的CookieHeaderValue[]{cookie}); } 返回响应; }
我发现这种方法非常不切实际(混合接口),您肯定可以访问
iaauthenticationfilter.authenticateSync中的响应(例如通过异步继续回调,或者通过能够访问操作结果(
IHttpActionResult
)在上下文中,就像在同一界面的
ChallengeAsync
方法中一样。

我的要求是添加一个标题,但添加cookie应该很容易

我对此采取了不同的方法。我将要添加的标题放入
context.Request.Properties
。然后,在
ChallengeAsync
(无论每个请求如何,都会调用它)中,通过
IHttpActionResult
检查属性是否存在,如果存在,则将其添加到标题中。大概是这样的:

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);
}
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;

}

我不太明白你的意思。我不需要服务器会话状态,我的cookie是一个简单的无状态令牌(如a),但我没有从
授权
头获取它,而是将它存储在HttpOnly cookie中。好的,我明白了,我刚刚发现:链接中的实现基于不同的模式:cient在“登录”URL上检索令牌,然后在每个后续请求中手动将其添加到
授权
标题中。我希望处理每个资源上的令牌生成(并使用cookie而不是
授权
头),因此使用
iaauthenticationfilter
(我认为
DelegatingHandler
并不真正适合设置用户身份)。