在 HttpAuthenticationContext 中为 IAuthenticationFilter 设置 Cookie 值

Setting Cookie values in HttpAuthenticationContext for IAuthenticationFilter

我需要在 WebAPI 管道的身份验证步骤中 read/write cookie。我为此创建了一个自定义过滤器。

为了遵守自托管概念,访问 cookie 并将其写入客户端的安全方式是什么? Rick Strahl 评论说,如果我们使用 HttpContext.Current.Response.Cookies.Add(),并且我的应用程序是自托管的,则上下文 may/will 不存在。

那么我如何使用 HttpAuthenticationContext 将 cookie 写到客户端并且仍然是自托管安全的?

HttpAuthenticationContext authContext;
authContext.ActionContext.Response.Headers.AddCookies(/*cookies */);

edit2

HttpAuthenticationContext authContext;
var myCookie = new CookieHeaderValue("key", "value")
authContext.ActionContext.Response.Headers.Add("Set-Cookie", myCookie.ToString());

编辑

AddCookie是位于System.Net.Http.Formatting.dll的扩展方法(自v5.2.2.0版本起),扩展方法由static class HttpResponseHeadersExtensions声明,位于namespace System.Net.Http.

  • 如果找不到扩展方法,请尝试定位 HttpResponseHeadersExtensions class.

  • 如果找不到 HttpResponseHeadersExtensions class,请尝试升级 Web Api 2 库。升级每个项目的 WebApi2 的所有 nuget 包的最有效方法(对于像我这样讨厌升级 nuget 包的人),是对术语 [=] 的 .config 文件执行全局 search/replace 45=](其中 x.x.x 是被 'version="5.2.2" targetFramework="net45"'

  • 替换的旧版本
  • 在最坏的情况下,如果你的老板或你妈妈不让你升级nuget包,你总是可以采取叛逆的态度反编译包含AddCookie的代码,看起来像这样:

        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Net.Http.Headers;
        using System.Net.Http.Properties;
        using System.Web.Http;
        namespace System.Net.Http
        {
            /// <summary> Provides extension methods for the <see cref="T:System.Net.Http.Headers.HttpResponseHeaders" /> class. </summary>
            [EditorBrowsable(EditorBrowsableState.Never)]
            public static class HttpResponseHeadersExtensions
            {
                private const string SetCookie = "Set-Cookie";
                /// <summary> Adds cookies to a response. Each Set-Cookie header is  represented as one <see cref="T:System.Net.Http.Headers.CookieHeaderValue" /> instance. A <see cref="T:System.Net.Http.Headers.CookieHeaderValue" /> contains information about the domain, path, and other cookie information as well as one or more <see cref="T:System.Net.Http.Headers.CookieState" /> instances. Each <see cref="T:System.Net.Http.Headers.CookieState" /> instance contains a cookie name and whatever cookie state is associate with that name. The state is in the form of a  <see cref="T:System.Collections.Specialized.NameValueCollection" /> which on the wire is encoded as HTML Form URL-encoded data.  This representation allows for multiple related "cookies" to be carried within the same Cookie header while still providing separation between each cookie state. A sample Cookie header is shown below. In this example, there are two <see cref="T:System.Net.Http.Headers.CookieState" /> with names state1 and state2 respectively. Further, each cookie state contains two name/value pairs (name1/value1 and name2/value2) and (name3/value3 and name4/value4). &lt;code&gt; Set-Cookie: state1:name1=value1&amp;amp;name2=value2; state2:name3=value3&amp;amp;name4=value4; domain=domain1; path=path1; &lt;/code&gt;</summary>
                /// <param name="headers">The response headers</param>
                /// <param name="cookies">The cookie values to add to the response.</param>
                public static void AddCookies(this HttpResponseHeaders headers, IEnumerable<CookieHeaderValue> cookies)
                {
                    if (headers == null)
                    {
                        throw Error.ArgumentNull("headers");
                    }
                    if (cookies == null)
                    {
                        throw Error.ArgumentNull("cookies");
                    }
                    foreach (CookieHeaderValue current in cookies)
                    {
                        if (current == null)
                        {
                            throw Error.Argument("cookies", Resources.CookieNull, new object[0]);
                        }
                        headers.TryAddWithoutValidation("Set-Cookie", current.ToString());
                    }
                }
            }
        }
    
    • 当你意识到在 webapi2 中添加 cookie 只需一行代码即可完成时,你会觉得花这么多时间寻找扩展方法有点愚蠢:

headers.TryAddWithoutValidation("Set-Cookie", new CookieHeaderValue("key", "value")); //where headers is a HttpResponseHeaders

您无法从 IAuthenticationFilter.AuthenticateAsync() 中访问 authContext.ActionContext.Response。好吧,实际上你可以,但只能设置一个新的响应并缩短管道的其余部分。

我遇到了同样的问题(需要在成功验证后设置 cookie),除了 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();

    // Here you get access to:
    // - The request (actionContext.Request)
    // - The response (response) and its cookies (response.Headers.AddCookies())
    // - The principal (actionContext.ControllerContext.RequestContext.Principal)

    return response;
}

参见: