自定义角色提供程序与ProtizeAttribute for MVC存在问题

时间:2014-11-28 04:30:57

标签: model-view-controller provider role authorize-attribute

我正在使用自定义角色提供程序开发MVC 5应用程序,但似乎AuthorizeAttribute从不调用我的客户角色提供程序,我的代码如下:

我的客户提供商:

namespace MyDomain
{
    public class CustomRoleProvider : RoleProvider
    {
         public override string[] GetRolesForUser(string username)
        {
            using (MyContext objContext = new MyContext())
            {
                var objUser = objContext.Users.FirstOrDefault(x => x.Username == username);
                if (objUser == null)
                {
                    return null;
                }
                else
                {
                    string[] ret = { objUser.Access_Levels.Name };
                    return ret;
                }
            }
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            var userRoles = GetRolesForUser(username);
            return userRoles.Contains(roleName);
        }
}

我的控制器:

[Authorize(Roles = "Administrator")]
public class AdminController : Controller

和Web.Config:

 <system.web>
    <roleManager defaultProvider="CustomRoleProvider" enabled="true" >
      <providers>
        <clear />
        <add name="CustomRoleProvider" type="Online_Storage_Portal.CustomRoleProvider"  cacheTimeoutInMinutes="30"/>
      </providers>
    </roleManager>
  </system.web>

此外,我的自定义角色提供程序与我的其他控制器位于同一项目中,我可以使用控制器中的以下代码调用我的自定义角色提供程序方法

String [] roles = Roles.GetRolesForUser(username)

但是[Authorize(Roles =“Administrator”)]的控制器总是将页面重定向到登录屏幕,即使用户登录和角色都是有价值的。

请帮助!!

1 个答案:

答案 0 :(得分:2)

我相信我找到了问题的根源。我将假设您正在使用Windows身份验证,并尝试使用自定义角色提供程序代替自动加载的Windows组。查看MVC AuthorizeAttribute源代码,您会发现它实际上正在调用Principal.IsInRole。每个MSDN:

  

InRole首先检查IsRoleListCached属性,以确定当前用户的角色名称的缓存列表是否可用。如果IsRoleListCached属性为true,则检查高速缓存的列表以查找指定的角色。如果IsInRole方法在缓存列表中找到指定的角色,则返回true。   如果IsInRole找不到指定的角色,则会调用默认Provider实例的GetRolesForUser方法,以确定用户名是否与配置的ApplicationName值的数据源中的角色相关联。

所以我猜测因为Principal是Windows Principal,它的角色是填充和缓存的。当IsInRole被调用时,它会说'嘿,我已经有了角色,为什么我会回到提供者那里再次获得它们?“

你可以做的事情就是这样:

protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
    {
        WindowsIdentity identity = HttpContext.Current.Request.LogonUserIdentity;
        HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new GenericIdentity(identity.Name), Roles.GetRolesForUser());
    }

这将从HttpContext中提取Windows标识,使用该名称从您的自定义提供程序显式获取角色,然后在请求上打一个新的GenericPrincipal。我进一步实现了一些逻辑来将角色存储在加密的cookie中,因此我们不必在每个请求上都使用角色提供程序。

 void Application_PostAuthenticateRequest()
    {
        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
        FormsAuthenticationTicket authTicket;
        if (authCookie == null || authCookie.Value == "")
        {
            string[] getRoles = Roles.GetRolesForUser();
            authTicket = new FormsAuthenticationTicket(1,
                User.Identity.Name,
                DateTime.Now,
                DateTime.Now.AddMinutes(20),
                true,
                String.Join(";", getRoles));

            string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
            authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
            HttpContext.Current.Response.Cookies.Add(authCookie);
        }
        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            return;
        }
        string[] roles = authTicket.UserData.Split(';');
        if (Context.User != null)
            Context.User = new System.Security.Principal.GenericPrincipal(Context.User.Identity, roles);
    }