2

I have roles on my ASP.NET application. I have figure the problem (I think). The problem every page in the applications uses role and permission. Therefore it uses the following function in page load

if (Roles.IsUserInRole("Admin")) { // display the page } else { // No }

I found a solution to my problem from this question Poor Performance with WindowsTokenRoleProvider

But there are a couple of differences 1. The above question uses WindowsTokenRoleProvider, I am using SqlRoleProvider

Because of the above problem, the above solution does not exactly work for me.

What I have done so far, and I am partially successful, I have derived a class from SqlRoleProvider and include this function which is the same from above question but modified. I changed web.config so that it looks like this

<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>

This is the function inside my class, which does get (executed only when a user login)

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();
    }

The problem is when Roles.IsUserInRole function calls the same old

System.Web.Security.Roles.IsUserInRole

function. I have even overloaded this function in my new class but it never gets executed. I am basically caching all the roles so that on each page refresh the application does not go search for all roles right from the start.

Do I need to derive another class from System.Web.Security.Roles.IsUserInRole? Has anyone done it.

Each page takes about 4-8 seconds on fresh which is too long. Code is in VS 2008, C# 3.5

4

5 回答 5

1

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

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

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

于 2012-07-09T22:04:26.620 回答
1

如果 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 
}
于 2012-07-10T04:10:23.810 回答
1
  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);
    }
    
于 2014-10-15T13:51:43.907 回答
0

By default there are a couple of providers added to your application besides those your specify. Removing them from the list of providers could certainly help out your performance, as you'll only be using the provider(s) that you want.

<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>

Also it is my understanding if you want to cache the results in a cookie you don't need to implement anything extra in your provider. The setting of the cacheRolesInCookie attribute to True directs .NET to handle the caching for you. As long as you are calling into the role provider using the System.Web.Security.Roles, .NET will handle the caching and putting the roles in the cookie.

I would try:

<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>
于 2012-07-09T21:53:01.393 回答
0

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

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

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

于 2012-07-09T22:37:13.450 回答