如何在Application_AuthenticateRequest中保留所需的特定于会话的值?

弗朗辛·德格罗德·泰勒

我们有一个非常大的Web应用程序,其中包含许多页面。这些页面要求我们知道用户的角色才能正确显示内容。因此,在Application_AuthenticationRequest中,我们有以下代码:

 var id = new GenericIdentity(Request.Headers["ceid"]);
 var rp = new MyRoleProvider();
 var principal = new GenericPrincipal(id, rp.GetRolesForUser(id.Name));
 Context.User = principal;

问题在于我们需要使用Web服务来获取角色,并且由于每次用户访问页面时都会进行该调用,因此Web服务被点击了太多次。

理想的解决方案是是否可以将角色存储在会话变量中,但是,会话在Application_AuthenticateRequest中不可用。我们考虑过在应用程序变量中存储一个字典,其中包含所有用户的条目,但我希望我们可以找到更好的解决方案。

有什么方法可以存储当前用户的角色,以使它们在Application_AuthenticationRequest中可用?我们非常安全意识;cookie是有效的选择吗?

亚历克斯·多梅尼西

我将创建一个静态类来保存所有当前用户的角色列表(基本上在您的Dictionary构想上进行扩展):如果列表中不存在给定ID的角色,我们将从Web服务中检索它们。否则,我们将返回存储的角色。

我们需要一种方法来在一定数量的用户不活动之后使数据过期(我们不想一直将所有用户的所有角色都保留在内存中),因此我们将实现自己的过期机制(在我的示例中为20分钟)。

这是我编写类的方法以及使用方法:

    /// <summary>
    /// This is the static class that will hold the roles for all active users (active users 
    /// are those that have been using the website within the last 20 minutes).
    /// </summary>
    public static class MyRoles
    {
        /// <summary>
        /// This class holds your roles
        /// </summary>
        private class Principal
        {
            private DateTime lastAccess; // Our expiration timer
            private System.Security.Principal.GenericPrincipal principal; // Our roles

            public Principal(System.Security.Principal.GenericPrincipal principal)
            {
                this.principal = principal;
                this.lastAccess = DateTime.Now;
            }

            public System.Security.Principal.GenericPrincipal GenericPrincipal
            {
                get
                { 
                    // We reset our timer. It will expire 20 minutes from now. 
                    this.lastAccess = DateTime.Now;

                    return principal;
                }
            }                                

            /// <summary>
            /// This tells us if a user has been active in the last 20 minutes
            /// </summary>
            public bool IsValid
            {
                get
                {
                    // Valid within 20 minutes from last call
                    return DateTime.Now <= this.lastAccess.AddMinutes(20);                        
                }
            }
        }

        /// <summary>
        /// This will hold IDs and related roles
        /// </summary>
        private static Dictionary<string, Principal> ids = new Dictionary<string, Principal>();

        /// <summary>
        /// Method to retrieve roles for a given ID
        /// </summary>
        /// <param name="header">Our ID</param>
        /// <returns></returns>
        public static System.Security.Principal.GenericPrincipal GetRoles(string header)
        {
            if (ids.ContainsKey(header) && ids[header].IsValid)
            {
                // We have roles for this ID
                return ids[header].GenericPrincipal;
            }
            else
            {
                // We don't have this ID (or it's expired) so get it from the web service
                var id = new System.Security.Principal.GenericIdentity(header);
                var principal = new System.Security.Principal.GenericPrincipal(id, new MyRoleProvider().GetRolesForUser(id.Name));

                // Store roles
                ids.Add(header, new Principal(principal));

                return principal;
            }
        }            
    }

    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        // This is how we use our class
        var principal = MyRoles.GetRoles(Request.Headers["ceid"]);
    }

请注意,我的示例代码未考虑存储GenericPrincipal实例列表所需的内存占用。我的目标仅仅是让您了解如何在不使用Session对象的情况下持久(和过期)用户数据。

20分钟的过期机制可以很容易地链接到Session_End事件,以便一旦会话结束(如果您使用的是InProc会话),它将使数据过期。

还请注意,与会话一样,此解决方案仅适用于单服务器环境。如果您具有负载平衡的环境(2个或更多Web服务器),则必须将数据持久保存到数据库中。

我希望这能够帮到你。如果没有,请告诉我原因,我会尽力帮助您:)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章