【问题标题】:Asp.net website very slow because of roles checkin on every pageAsp.net 网站非常慢,因为每个页面上的角色检查
【发布时间】:2012-07-09 08:10:47
【问题描述】:

我在我的 ASP.NET 应用程序中有角色。我已经解决了问题(我认为)。应用程序中的每个页面都使用角色和权限的问题。因此它在页面加载中使用了以下函数

if (Roles.IsUserInRole("Admin")) { // 显示页面 } 别的 { // 不 }

我从这个问题Poor Performance with WindowsTokenRoleProvider找到了我的问题的解决方案

但是有一些不同之处 1.以上问题使用WindowsTokenRoleProvider,我使用的是SqlRoleProvider

由于上述问题,上述解决方案并不完全适合我。

到目前为止我所做的,并且我部分成功,我从 SqlRoleProvider 派生了一个类,并包含了这个与上述问题相同但经过修改的函数。我更改了 web.config,使其看起来像这样

<roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPR0L3S" cookieTimeout="117" cookieSlidingExpiration="true" cookieProtection="All" createPersistentCookie="false" defaultProvider="CustomSqlRoleProvider">
            <providers>
                <add name="CustomizedRoleProvider" type="CustomSqlRoleProvider" connectionStringName="PEGConn" applicationName="/CRM"/>
            </providers>
        </roleManager>

这是我类中的函数,它确实得到(仅在用户登录时执行)

public override string[] GetRolesForUser(string username)
    {
        // Will contain the list of roles that the user is a member of
        List<string> roles = null;

        // Create unique cache key for the user
        string key = String.Concat(username, ":", base.ApplicationName);

        // Get cache for current session
        Cache cache = HttpContext.Current.Cache;

        // Obtain cached roles for the user
        if (cache[key] != null)
        {
            roles = new List<string>(cache[key] as string[]);
        }

        // Was the list of roles for the user in the cache?



        if (roles == null)
        {
            string[] AllRoles = GetAllRoles();
            roles = new List<string>();

            // For each system role, determine if the user is a member of that role
            foreach (String role in AllRoles)
            {
                if (base.IsUserInRole(username, role))
                {
                    roles.Add(role);
                }
            }

            // Cache the roles for 1 hour
            cache.Insert(key, roles.ToArray(), null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
        }

        // Return list of roles for the user
        return roles.ToArray();
    }

问题是当 Roles.IsUserInRole 函数调用相同的旧时

System.Web.Security.Roles.IsUserInRole

功能。我什至在我的新类中重载了这个函数,但它永远不会被执行。我基本上缓存了所有角色,以便在每个页面刷新时应用程序不会从一开始就搜索所有角色。

我需要从System.Web.Security.Roles.IsUserInRole 派生另一个类吗?有人做过吗?

每页刷新大约需要 4-8 秒,这太长了。代码在 VS 2008 中,C# 3.5

【问题讨论】:

  • 你确定是这个问题吗?您是否尝试过取消角色检查并为您的应用程序计时?
  • 为什么要显式调用 base.IsUserInRole(username, role) ?这将始终调用基类实现。如果您在派生类上实现了 IsUserInRole,请改为调用它!您是否对您的应用进行了分析以确定哪种方法花费的时间最多?
  • 你确定这真的是问题吗?我有很多检查安全性的页面,加载它们不需要 4 到 8 秒。这应该是一个数据库查询,最大值。这不应该需要 4 秒。在为此付出很多努力之前,我会尝试注释掉您的安全检查,这样您就可以确定它需要 0 时间,并查看页面加载速度。
  • 我确实分析了我的应用程序,并且一直在这里花费的时间最多。禁用它会使其相当快。但我可以仔细检查。不久前做过。
  • @dash 这是我的问题。在每个页面上使用using System.Web.Security;,我应该将其更改为我自己的自定义类文件名吗?这是我需要帮助的地方。

标签: c# asp.net roles


【解决方案1】:

默认情况下,除了您指定的供应商之外,您的应用程序中还会添加几个供应商。从提供者列表中删除它们当然可以帮助您提高性能,因为您只会使用您想要的提供者。

<system.web>
  <membership defaultProvider="MyMembershipProvider">
    <providers>
      <clear />
      <add name="MyMembershipProvider" type="MyMembershipProvider" applicationName="My App" />
    </providers>
  </membership>
  <roleManager defaultProvider="MyRoleProvider" enabled="true" cacheRolesInCookie="true" cookieTimeout="60">
    <providers>
      <clear />
      <add name="MyRoleProvider" type="MyRoleProvider" applicationName="My App" />
    </providers>
  </roleManager>
</system.web>

另外,我的理解是,如果您想将结果缓存在一个 cookie 中,您不需要在您的提供程序中实现任何额外的东西。 cacheRolesInCookie 属性设置为 True 指示 .NET 为您处理缓存。只要您使用 System.Web.Security.Roles 调用角色提供程序,.NET 就会处理缓存并将角色放入 cookie。

我会尝试:

<system.web>
  <roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPR0L3S" cookieTimeout="117" cookieSlidingExpiration="true" cookieProtection="All" createPersistentCookie="false" defaultProvider="SqlRoleProvider">
    <providers>
      <clear />
      <add name="SqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="PEGConn" applicationName="/CRM"/>
    </providers>
  </roleManager>
</system.web>

【讨论】:

    【解决方案2】:

    我认为这也可能隐藏了您的应用程序设计的一个基本问题。您应该遵守 DRY 原则。不要重复自己,即不要在每一页上重复相同的查找/代码。我建议使用会话变量,以便您可以“缓存”这些昂贵的角色查找。以下是使用会话变量的快速指南:

    http://msdn.microsoft.com/en-us/library/ms178581.aspx

    旁注。我看到您正在使用 cookie 来存储您的这些“角色”。这听起来不太安全,因此我假设安全性不是本练习的主要目标。

    【讨论】:

      【解决方案3】:

      首先,你做错了。您不应该在 Page_Load 中放置任何与此相关的内容。如果您的角色是静态的,您应该使用授权密钥 web.config,如果它们是动态的,您应该在 global.asax Application_AuthorizeRequest 事件中执行此操作。

      这将使页面生命周期更进一步,使其更安全且资源浪费更少。

      其次,您需要调查这慢的原因。您可以通过在 cookie 中缓存角色来解决它(JG 提到了如何做到这一点),但实际上,请查看数据库本身。角色查找不应该花费很长时间。执行支持角色查找的存储过程(它们在数据库中)并查看执行是否缓慢。

      【讨论】:

        【解决方案4】:

        如果 Roles.IsUserInRole("Admin") 需要时间,您可以检查一次用户的角色(在登录时)并将值保存为会话对象。

        const string IS_ADMIN_KEY; //this can be on a base class of page / master page
        
        Session[IS_ADMIN_KEY] = Roles.IsUserInRole("Admin"); // do this when logging in
        
        //Add this to page load
        bool isAdmin = Session[IS_ADMIN_KEY]; 
        if(isAdmin)) 
        { 
           // display the page 
        } else 
        { 
           // don't display the page 
        }
        

        【讨论】:

        • 我建议不要以这种方式在会话中存储和检索角色。特别是如果您使用默认的 Web 控件来创建和管理用户。您仍然可以将结果存储在会话中,但如果这样做,则无需将结果缓存在 cookie 中。我将实现一个角色提供者,它派生自您希望在其上引入会话缓存的角色提供者。在那里你会检查会话,如果结果存在则返回它,否则调用基础,将结果存储在会话中,然后返回结果。
        【解决方案5】:
        1. 我建议你利用 GetRolesForUser 的基类实现,而不是重复调用 IsUserInRole,它会在每次调用时访问数据库

          public override string[] GetRolesForUser(string username)
          {
              // Create unique cache key for the user
              string key = String.Concat(username, ":", base.ApplicationName);
          
              string[] roles = HttpRuntime.Cache[key] as string[];
          
              if (roles != null)
              {
                  roles = base.GetRolesForUser(username);
          
                  HttpRuntine.Cache.Insert(key, roles.ToArray(), ...);
              }
          
          }
          
        2. 我还会考虑使用滑动到期,其值可以配置并默认为 30 分钟左右 - 类似于默认的 RoleManagerSection.CookieTimeout)。这可能是您 1 小时绝对到期的补充。

        3. 最后,您应该覆盖 IsUserInRole 以使用您的缓存。可以这样实现:

          public override bool IsUserInRole(string username, string roleName)
          {
              // Case-insensitive comparison for compatibility with RolePrincipal class
              return thisGetRolesForUser(username).Contains(roleName, 
                                  StringComparer.OrdinalIgnoreCase);
          }
          

        【讨论】:

          猜你喜欢
          • 2021-07-15
          • 2016-06-11
          • 1970-01-01
          • 2014-11-10
          • 1970-01-01
          • 2014-09-20
          • 2015-02-20
          • 2010-11-18
          • 2014-08-11
          相关资源
          最近更新 更多