如何在ASP.NET Membership Cookie中存储自定义数据

5
有人能给我一个例子(或指点一下方向),如何在ASP.NET Membership cookie中存储自定义数据吗?
我需要添加一些自定义属性,例如UserID和URLSlug到cookie,并且能够以与检索用户名相同的方式检索信息。
编辑:
我使用了Code Poet的示例,并得出了以下结论。
当我在Dim SerializedUser As String = SerializeUser(userData)处设置断点时,userData的值是正确的。它拥有我期望的所有属性。
我现在遇到的问题是,当我到达Dim userdata As String = authTicket.UserData(断点)时,值为""。我很想弄清楚我做错了什么。
这是代码。
Imports System
Imports System.Web
Imports System.Web.Security

Namespace Utilities.Authentication
    Public NotInheritable Class CustomAuthentication
        Private Sub New()
        End Sub

        Public Shared Function CreateAuthCookie(ByVal userName As String, ByVal userData As Domain.Models.UserSessionModel, ByVal persistent As Boolean) As HttpCookie

            Dim issued As DateTime = DateTime.Now
            ''# formsAuth does not expose timeout!? have to hack around the
            ''# spoiled parts and keep moving..
            Dim fooCookie As HttpCookie = FormsAuthentication.GetAuthCookie("foo", True)
            Dim formsTimeout As Integer = Convert.ToInt32((fooCookie.Expires - DateTime.Now).TotalMinutes)

            Dim expiration As DateTime = DateTime.Now.AddMinutes(formsTimeout)
            Dim cookiePath As String = FormsAuthentication.FormsCookiePath

            Dim SerializedUser As String = SerializeUser(userData)

            Dim ticket = New FormsAuthenticationTicket(0, userName, issued, expiration, True, SerializedUser, cookiePath)
            Return CreateAuthCookie(ticket, expiration, persistent)
        End Function

        Public Shared Function CreateAuthCookie(ByVal ticket As FormsAuthenticationTicket, ByVal expiration As DateTime, ByVal persistent As Boolean) As HttpCookie
            Dim creamyFilling As String = FormsAuthentication.Encrypt(ticket)
            Dim cookie = New HttpCookie(FormsAuthentication.FormsCookieName, creamyFilling) With { _
             .Domain = FormsAuthentication.CookieDomain, _
             .Path = FormsAuthentication.FormsCookiePath _
            }
            If persistent Then
                cookie.Expires = expiration
            End If

            Return cookie
        End Function


        Public Shared Function RetrieveAuthUser() As Domain.Models.UserSessionModel
            Dim cookieName As String = FormsAuthentication.FormsCookieName
            Dim authCookie As HttpCookie = HttpContext.Current.Request.Cookies(cookieName)
            Dim authTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
            Dim userdata As String = authTicket.UserData

            Dim usersessionmodel As New Domain.Models.UserSessionModel
            usersessionmodel = DeserializeUser(userdata)
            Return usersessionmodel
        End Function


        Private Shared Function SerializeUser(ByVal usersessionmodel As Domain.Models.UserSessionModel) As String
            Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
            Dim mem As New IO.MemoryStream
            bf.Serialize(mem, usersessionmodel)
            Return Convert.ToBase64String(mem.ToArray())
        End Function

        Private Shared Function DeserializeUser(ByVal serializedusersessionmodel As String) As Domain.Models.UserSessionModel
            Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
            Dim mem As New IO.MemoryStream(Convert.FromBase64String(serializedusersessionmodel))
            Return DirectCast(bf.Deserialize(mem), Domain.Models.UserSessionModel)
        End Function
    End Class
End Namespace

这里是我创造所有魔法的地方。这种方法在“BaseController”类中,继承了System.Web.Mvc.Controller

Protected Overrides Function CreateActionInvoker() As System.Web.Mvc.IActionInvoker

            If User.Identity.IsAuthenticated Then ''# this if statement will eventually also check to make sure that the cookie actually exists.

                Dim sessionuser As Domain.Models.UserSessionModel = New Domain.Models.UserSessionModel(OpenIdService.GetOpenId(HttpContext.User.Identity.Name).User)
                HttpContext.Response.Cookies.Add(UrbanNow.Core.Utilities.Authentication.CustomAuthentication.CreateAuthCookie(HttpContext.User.Identity.Name, sessionuser, True))
            End If
End Function

以下是我尝试检索信息的方法。

 Dim user As Domain.Models.UserSessionModel = CustomAuthentication.RetrieveAuthUser

我找到了问题的答案。上面的代码是有效的,但是 Response.Cookies.Add 没有起作用,因为它发生在错误的位置。Cookie 已经在身份验证步骤中创建了。我必须删除在 Auth 步骤中创建 Cookie 的代码,并将其替换为 CreateActionInvoker 方法中的代码。 - Chase Florell
2个回答

7

首先,ASP.Net成员资格提供程序不会写任何cookie,认证cookie是由FormsAuthentication编写的。

其次,为什么要干涉认证cookie。您可以在单独的cookie中完成此操作。以下是如何执行此操作的方法。

将键值写入cookie。

//create a cookie
HttpCookie myCookie = new HttpCookie("myCookie");

//Add key-values in the cookie
myCookie.Values.Add("UserId", "your-UserId");
myCookie.Values.Add("UrlSlug", "your-UrlSlug");

//set cookie expiry date-time, if required. Made it to last for next 12 hours.
myCookie.Expires = DateTime.Now.AddHours(12);

//Most important, write the cookie to client.
Response.Cookies.Add(myCookie);

从cookie中读取键值对。

//Assuming user comes back after several hours. several < 12.
//Read the cookie from Request.
HttpCookie myCookie = Request.Cookies["myCookie"];
if (myCookie == null)
{
    //No cookie found or cookie expired.
    //Handle the situation here, Redirect the user or simply return;
}

//ok - cookie is found.
//Gracefully check if the cookie has the key-value as expected.
if (!string.IsNullOrEmpty(myCookie.Values["UserId"]))
{
    string UserId= myCookie.Values["UserId"].ToString();
    //Yes UserId is found. Mission accomplished.
}

if (!string.IsNullOrEmpty(myCookie.Values["UrlSlug"]))
{
    string UrlSlug = myCookie.Values["UrlSlug"].ToString();
    //Yes key2 is found. Mission accomplished.
}

如果你必须打扰认证cookie,虽然不建议这样做,那么你可以按照以下步骤进行操作。
将键值写入cookie。
//create a cookie
HttpCookie myCookie = FormsAuthentication.GetAuthCookie("UserName", true);

//Add key-values in the cookie
myCookie.Values.Add("UserId", "your-UserId");
myCookie.Values.Add("UrlSlug", "your-UrlSlug");

//set cookie expiry date-time, if required. Made it to last for next 12 hours.
myCookie.Expires = DateTime.Now.AddHours(12);

//Most important, write the cookie to client.
Response.Cookies.Add(myCookie);

从 cookie 中读取键值对。

//Assuming user comes back after several hours. several < 12.
//Read the cookie from Request.
HttpCookie myCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (myCookie == null)
{
    //No cookie found or cookie expired.
    //Handle the situation here, Redirect the user or simply return;
}

//ok - cookie is found.
//Gracefully check if the cookie has the key-value as expected.
if (!string.IsNullOrEmpty(myCookie.Values["UserId"]))
{
    string UserId= myCookie.Values["UserId"].ToString();
    //Yes UserId is found. Mission accomplished.
}

if (!string.IsNullOrEmpty(myCookie.Values["UrlSlug"]))
{
    string UrlSlug = myCookie.Values["UrlSlug"].ToString();
    //Yes key2 is found. Mission accomplished.
}

抱歉出现错误。是的,我在我的应用程序中使用了一种表单身份验证方式。感谢您提供详细的建议,我明天会看一下(该睡觉了)。 - Chase Florell
我实际上在使用DotNetOpenAuth处理我的会员相关事务(没有找到ASP.NET会员)。但是我发现DotNetOpenAuth在某种程度上使用了表单验证cookie... 我只是想顺便利用一下。 - Chase Florell
另外,为什么不建议“干扰”认证 cookie? - Chase Florell
有没有一种方法可以将其添加到IPrincipal扩展中,以便我(例如)可以调用User.Identity.Slug来获取用户Slug? - Chase Florell
1
我认为这是错误的建议。你可以看到我的回答来了解原因。@rock,干杯。 - Sky Sanders

4
根据场景,使用单独的cookie可能是可行的选择,但在我看来,由于需要管理多个cookie以及管理cookie的生命周期等多个方面的原因,这种方式并不是最优的。
将自定义信息整合到表单票证中最可靠的策略是利用票证的“userData”字段。这正是其存在的目的。
您可以轻松地将自定义数据存储在票证的“userData”字段中。
关于存储在票证中的数据大小,有几个需要注意的问题,可以在这里了解详细信息。
这里则提供了一个小型类,可帮助存储自定义数据到表单票证中的任务。

所以,如果我有一个带有自定义数据的小类对象,我需要持久访问其中的 ID、RegionID、Username、Slug,我是将其作为逗号分隔字符串发送到 userData 对象中,还是需要对其进行序列化并将其放入其中? - Chase Florell
@rock,如果你所提供的对象如此简单,使用csv应该没有问题。不需要过度设计。但是你需要注意对最终的“userdata”值进行url编码,因为嵌入的逗号和分号会破坏cookie。当然,这意味着您需要对其进行url解码以重新激活,同时要注意嵌入的逗号。您期望的数据将决定您需要维护的意识水平。祝好运。 - Sky Sanders
是的,看起来是这样。现在我需要测试它的性能。我在每个页面加载时都会检查那个 cookie 中的某些项目,我想知道它会对性能造成多大影响。 - Chase Florell
@rock - 而且从更宏观的角度来看,如果有足够的内存,与网络和磁盘IO相比,CPU使用率很少成为瓶颈,这是我个人的意见。 - Sky Sanders
@rock - 除非我有一个真正令人信服的理由来在会话中维护状态,否则我会每次将其卸载到客户端。你可能忽略了一个微妙的问题,即会话!=表单票证,而且生命周期无论你如何努力都不会同步,虽然你可以找到一个合适的解决方法,但到那时,代码的大小和复杂性的增加会让你转身向后退,将你所能的一切都藏在表单票证中。 - Sky Sanders
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接