gpt4 book ai didi

c# - 在 ASP.NET Core 2 中强制使用已知 header 的特定大小写

转载 作者:行者123 更新时间:2023-12-02 20:31:47 33 4
gpt4 key购买 nike

我有一个带有 Kestrel 的 ASP.NET Core 2 应用程序。该应用程序已部署到 AWS Lambda/API Gateway。除了让一切大不相同的小细节外,一切都按预期工作。

对我的应用程序的某些请求需要发出多个与安全相关的 Set-Cookie header 。由于数据在 API 网关和 Lambda 之间传递的方式,重复的 header 名称被连接在一起,这导致 Set-Cookie header 无效并且浏览器拒绝接受它。

A suggested solution克服此限制的方法是使用仅因大小写而异的多个 header 名称:Set-CookieSet-cookieset-cookie.. .

我知道这是一个 hacky 解决方案,但如果它有效,它应该足够好,同时 AWS 修复了这个限制。

但是,当使用 HttpContext.Response.Headers.Add(name, value) 时,已知 header 名称被规范化并成为常规的重复 header 。

是否有可能绕过这种规范化机制或以其他方式实现最终目标?

最佳答案

当我开始研究这个问题时,我认为这很容易。经过半天的研究(酷到放假了),终于可以分享结果了。

HttpContext.Response.Headers 的类型为 IHeaderDictionary。默认情况下,在 Kestrel 上的 ASP.NET Core 应用程序中,FrameResponseHeaders实现被使用。主要逻辑位于 FrameHeaders 基类中。这个 header 字典针对设置/获取常用的标准 http header 进行了高度优化。这是一个 code snippet处理设置 cookie(AddValueFast 方法):

if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase))
{
if ((_bits & 67108864L) == 0)
{
_bits |= 67108864L;
_headers._SetCookie = value;
return true;
}
return false;
}

StringComparison.OrdinalIgnoreCase 用于键比较而言,您不能设置另一个仅大小写不同的 cookie header 。这是有道理的,因为 HTTP headers are case-insensitive .但让我们努力克服它。

此处显而易见的解决方案是将 IHeaderDictionary 的实现替换为区分大小写的实现。 ASP.NET Core 为此包含很多接缝和扩展点,从 IHttpResponseFeature 开始包含可设置的 Headers 属性并以替换 HttpContext 的实现的可能性结束。

不幸的是,在 Kestrel 上运行时,所有这些替换都无法解决问题。如果您检查 Frame 的源代码负责编写 HTTP 响应 header 的类,您会看到它自己创建了 FrameResponseHeaders 的实例,并且不尊重通过 IHttpResponseFeatureHttpContext 设置的任何其他实例.Response.Headers:

protected FrameResponseHeaders FrameResponseHeaders { get; } = new FrameResponseHeaders();

所以我们应该回到 FrameResponseHeaders 及其基础 FrameHeaders 类并尝试调整它们的行为。

FrameResponseHeaders 类使用已知 header 的快速设置(参见上面的 AddValueFast),但将所有其他未知 header 存储在 MaybeUnknown 字段中:

protected Dictionary<string, StringValues> MaybeUnknown;

初始化为:

MaybeUnknown = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);

我们可以尝试绕过快速 header 设置并将它们直接添加到 MaybeUnknown 字典中。但是,我们应该将使用 StringComparer.OrdinalIgnoreCase 比较器创建的字典替换为区分大小写的默认实现。

MaybeUnknown 是一个 protected 字段,我们不能让 Kestrel 使用我们的自定义实现来保存类。这就是为什么我们不得不通过反射来设置这个字段。

我已将所有这些脏代码放入 FrameHeaders 的扩展类中:

public static class FrameHeadersExtensions
{
public static void MakeCaseInsensitive(this FrameHeaders target)
{
var fieldInfo = GetDictionaryField(target.GetType());
fieldInfo.SetValue(target, new Dictionary<string, StringValues>());
}

public static void AddCaseInsensitiveHeader(this FrameHeaders target, string key, string value)
{
var fieldInfo = GetDictionaryField(target.GetType());
var values = (Dictionary<string, StringValues>)fieldInfo.GetValue(target);
values.Add(key, value);
}

private static FieldInfo GetDictionaryField(Type headersType)
{
var fieldInfo = headersType.GetField("MaybeUnknown", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
{
throw new InvalidOperationException("Failed to get field info");
}

return fieldInfo;
}
}

MakeCaseInsensitiveMaybeUnknown 替换为区分大小写的字典。AddCaseInsensitiveHeader 绕过快速 header 设置将 header 直接添加到 MaybeUnknown 字典。

剩下的部分只是在controller中适当的地方调用这些方法:

[Route("api/[controller]")]
public class TestController : Controller
{
[NonAction]
public override void OnActionExecuting(ActionExecutingContext context)
{
var responseHeaders = (FrameResponseHeaders)HttpContext.Response.Headers;
responseHeaders.MakeCaseInsensitive();
}

// GET api/values
[HttpGet]
public string Get()
{
var responseHeaders = (FrameResponseHeaders)HttpContext.Response.Headers;
responseHeaders.AddCaseInsensitiveHeader("Set-Cookie", "Cookies1");
responseHeaders.AddCaseInsensitiveHeader("SET-COOKIE", "Cookies2");
return "Hello";
}
}

这是结果标题集:

enter image description here

描述的解决方案是一个非常肮脏的 hack。它只能与 Kestrel 一起使用, future 的版本可能会发生变化。如果 Kestrel 完全支持 ASP.NET 接缝,一切都会变得更加简单和清晰。但是,如果此时您别无选择,我希望这对您有所帮助。

关于c# - 在 ASP.NET Core 2 中强制使用已知 header 的特定大小写,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48447548/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com