MVC4 - 在表单身份验证票证中设置自定义用户数据会导致表单身份验证 cookie 无法设置?

MVC4 - Setting custom user data in forms authentication ticket causes the forms authentication cookie to not get set?

我的遗留 MVC4 应用程序使用表单身份验证。当我尝试创建以下自定义 FormsAuthenticationTicket 时,cookie 会在响应中返回,但从未由浏览器设置。请注意,我试图在 cookie 中存储一个 OpenID Connect id_token,依赖于 FormsAuthenticationTicket class 提供的加密。

//Yes you can decode it - it's just test data
string idToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA1M2JjYTgzNzZmZjhlNTM5MWVkYzMxYWJkMjU5YzBjIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDc1ODExMjcsImV4cCI6MTUwNzU4MTQyNywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiaW1wbGljaXQiLCJub25jZSI6IjE1MDc1ODExMjA5MDMwMDQ0MDUzNDkzNjk3ODUzMTYiLCJpYXQiOjE1MDc1ODExMjcsImF0X2hhc2giOiJVWm5SeU1pXzVyUEN6NWduYmt5c09BIiwic2lkIjoiOTExYjI1OGQ5OGNiYzRlYzVkYTFiNmFkYzhiMmRjNTUiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTA3NTgxMTI3LCJpZHAiOiJsb2NhbCIsIm5hbWUiOiJ4Iiwid2Vic2l0ZSI6Imh0dHBzOi8vYWxpY2UuY29tIiwicm9sZSI6ImFkbWluIiwibW9kZWxBY2Nlc3MiOlsiMTIzNCIsIjU2NzgiXSwiY29ubmVjdGlvblN0cmluZyI6InNvbWVNb2RlbENvbm5lY3Rpb25TdHJpbmciLCJhbXIiOlsicHdkIl19.SwTIU1dP1FifCcXVNHkbIGshQiGIjfaa7UAWOrtqKb-FqMMrkvJx_Wa3W19r6NeNwc8mo2go6AFwwu_WM0TF1VJBO1pfmvX35oKgjdTTSqrSmMo5R9_rcywm5YKwVYzmvDqRjPfhZksXkIOuTIk3JOemLrKqw6VIHPyFYV6ZYSK6ZxTpxx50Yz90MmEOBDsTc0GZpQbeZmzyDkBe-iD9uVnlPN2UHz_UuMF__yfmzjGROKLpvem36TKSMa1mEJE7DVxJkexmbxQe3CVwZeIU3iPKloabSReaLCJLqINeI0ikGa4x6PbgfjiP1TPVhIP6i8zUp47lSavGgyy0XVFGtQ";

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                            1,
                            model.UserName,
                            DateTime.Now,
                            DateTime.Now.AddDays(30),
                            true,
                            idToken,
                            FormsAuthentication.FormsCookiePath);
                        // Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
this.Response.Cookies.Add(new HttpCookie("TEST", encTicket));

一开始我以为是因为篇幅问题。但我读到 cookie 的最大长度是 4096 字节。 idToken只有983字节。

有趣的是,如果我将数据变小(将 idToken 更改为 684 字节),一切都会按预期进行。这是一个工作示例:

//Shortened the idToken (for the sake of the example)
string idToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA1M2JjYTgzNzZmZjhlNTM5MWVkYzMxYWJkMjU5YzBjIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDc1ODExMjcsImV4cCI6MTUwNzU4MTQyNywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiaW1wbGljaXQiLCJub25jZSI6IjE1MDc1ODExMjA5MDMwMDQ0MDUzNDkzNjk3ODUzMTYiLCJpYXQiOjE1MDc1ODExMjcsImF0X2hhc2giOiJVWm5SeU1pXzVyUEN6NWduYmt5c09BIiwic2lkIjoiOTExYjI1OGQ5OGNiYzRlYzVkYTFiNmFkYzhiMmRjNTUiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTA3NTgxMTI3LCJpZHAiOiJsb2NhbCIsIm5hbWUiOiJ4Iiwid2Vic2l0ZSI6Imh0dHBzOi8vYWxpY2UuY29tIiwicm9sZSI6ImFkbWluIiwibW9kZWxBY2Nlc3MiOlsiMTIzNCIsIjU2NzgiXSwiY29ubmVjdGlvblN0cmluZyI6InNvbWVNb2RlbENvbm5lY3Rpb25TdHJpbmciLCJhbXIiOlsicHdkIl19.SwTIU1dP1FifCcXVNHkbIGshQiGIjfaa7UAWOrtqKb-";

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                            1,
                            model.UserName,
                            DateTime.Now,
                            DateTime.Now.AddDays(30),
                            true,
                            idToken,
                            FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
this.Response.Cookies.Add(new HttpCookie("TEST", encTicket));

我解密数据如下:

HttpCookie cookie = HttpContext.Current.Request.Cookies["TEST"];
if (cookie != null)
{
    Debug.WriteLine("Found Cookie Application_PostAuthenticateRequest");

    // Get the forms authentication ticket.
    FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookie.Value);
    if (authTicket == null)
    {
          Debug.WriteLine("auth ticket was null");
    }
    else
    {
          Debug.WriteLine("printing auth ticket");
          Debug.WriteLine(authTicket.UserData);
    }
}
else
{
     Debug.WriteLine("No Cookie Application_PostAuthenticateRequest");
}

尽管 cookie 远低于最大限制,但为什么第一个示例无法正常工作?

问题是加密之后数据太大了。您必须限制放入 FormsAuthenticationTicket 中的数据量。如果您在限制范围内,那么一切都会按预期工作。

我在深入研究 ASP.NET 身份验证 cookie 时遇到了这个问题。根本原因是 ASP.NET 用来将其转换为 cookie 的编码导致您的 983b 数据在最终 over-the-wire cookie 中占用了 3936b。

原因是双重的。

1) ASP.NET cookie 将数据字符串作为 UTF-16LE 字符串序列化到 cookie 负载中。 see reference source

这导致您的 983b 被写为 7 位编码长度( 0x5707 ),然后是 1966b(983 个 UTF-16LE 字符),在编码 1968b 之前的总大小,2:1 增加存储空间。

2) 整个 cookie 加密后被写为十六进制字符串,这意味着 1968b 中的每一个在最终 cookie 中都表示为两个字符 0..9A..F(例如:A1B2C3..)导致 cookie 的总大小为 3936b,存储量增加 4:1。

当然还有该 cookie 中的其他内容,例如到期日期、用户名等,这意味着您的 983b 数据很快就会超过 4kb cookie 的限制。

这似乎可以解释有关用户数据的 dotnet 评论 on the docs page

You should limit the amount of data stored in the UserData property. You must ensure that the size of the UserData property does not result in an invalid cookie or an excessively long URL.

因为看起来每 1 个字节的用户数据都会产生 4 个字节的 cookie。

分享了很久,因为我希望这个 post 48 小时前就存在了。