Переопределите User.IsInRole и [Authorize(Roles = "Admin")] для приложения MVC4

Я создал пользовательский поставщик ролей для своего приложения MVC4, где мне удалось переопределить методы CreateRole, GetAllRoles и RoleExists и связать их с моей существующей базой данных следующим образом:

namespace Project.Providers
{
  public class MyProvider : System.Web.Security.SqlRoleProvider
  {
    private MyContext dbcontext = new MyContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString);
    private Repository<MyUser> userRepository;
    private Repository<Role> roleRepository;

    public MyProvider()
    {
        this.userRepository = new Repository<MyUser>(dbcontext);
        this.roleRepository = new Repository<Role>(dbcontext);
    }

    public override string[] GetAllRoles()
    {
        IEnumerable<Role> dbRoles = roleRepository.GetAll();
        int dbRolesCount = roleRepository.GetAll().Count();
        string[] roles = new string[dbRolesCount];
        int i = 0;
        foreach(var role in dbRoles)
        {
            roles[i] = role.Name;
            i++;
        }
        return roles;
    }

    public override bool RoleExists(string roleName)
    {
        string[] roles = { "Admin", "User", "Business" };
        if(roles.Contains(roleName))
            return true;
        else
            return false;
    }

    public override void CreateRole(string roleName)
    {
        Role newRole = new Role();
        newRole.Name = roleName;
        roleRepository.Add(newRole);
        roleRepository.SaveChanges();
    }

    public override bool IsUserInRole(string userName, string roleName)
    {
        MyUser user = userRepository.Get(u => u.Username == userName).FirstOrDefault();
        Role role = roleRepository.Get(r => r.Name == roleName).FirstOrDefault();
        if (user.RoleID == role.RoleID)
            return true;
        else
            return false;
    }
  }
}

Я не смог найти способ переопределить

User.IsInRole(string roleName)

Что еще я должен сделать, чтобы при использовании:

[Authorize(Roles = "Admin")]

Он будет основан на установленном мной поставщике ролей, а не на asp по умолчанию.

Мой класс пользователя теперь выглядит следующим образом:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Collections;
using System.Security.Principal;

namespace Project.Data
{
  public class MyUser : IPrincipal
  {
    [Key]
    public int UserID { get; set; }

    [StringLength(128)]
    public string Username { get; set; }              

    .....other properties

    public IIdentity Identity { get; set; }

    public bool IsInRole(string role)
    {
        if (this.Role.Name == role)
        {
            return true;
        }
        return false;
    }

    public IIdentity Identity
    {
        get { throw new NotImplementedException(); }
    }
  }
}

Моя трассировка стека выглядит следующим образом:

System.Web.Security.RolePrincipal.IsInRole(String role) 

Поэтому я попытался реализовать пользовательский RolePrincipal таким же образом, как я настраивал пользовательский поставщик. Есть идеи, как мне это сделать? Не уверен, что конструктор params требует. Вот моя попытка:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration.Provider;
using Project.Data;
using System.Web.Security;
using System.Security.Principal.IIdentity;

namespace Project.Principal
{
  public class MyPrincipal : System.Web.Security.RolePrincipal
  {
    private MyContext dbcontext = new MyContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString);
    private Repository<MyUser> userRepository;
    private Repository<Role> roleRepository;        

    public MyPrincipal()
    {
        this.userRepository = new Repository<MyUser>(dbcontext);
        this.roleRepository = new Repository<Role>(dbcontext);
    }

    public override bool IsInRole(string role)
    {
        //code to be added
        return true;
    }
}

}

3 ответа

Вам просто нужно переопределить метод GetRolesForUser в своем поставщике пользовательских ролей вместо более логичного IsUserInRole, потому что это то, что вызывается реализацией по умолчанию, которая выполняет нежелательное кэширование.

Вы переопределяете IsInRole в своем классе IPrincipal, мой в EF выглядит так:

public class MyUser : IPrincipal {
    //Properties
    ...
    public bool IsInRole(string role) {
        if (Roles.Any(m=>m.NameKey.ToLower().Equals(role.ToLower()))) {
            return true;
        }
        return false;
    }
}

Затем, как только вы добавите соответствующие разделы в свою веб-конфигурацию как для RoleProvider, так и для MembershipProvider, вы сможете использовать атрибут Authorize.

ОБНОВЛЕНИЕ в ответ на ваши комментарии

Web Config должен выглядеть так:

...
<authentication mode="Forms">
  <forms loginUrl="~/Login" timeout="2880"></forms>
</authentication>
<authorization>
</authorization>

..

<membership defaultProvider="MyMembershipProvider">
  <providers>
    <add name="MyMembershipProvider" type="MyApp.Infrastructure.MyMembershipProvider" connectionStringName="connectionstring" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" />
  </providers>
</membership>
<roleManager defaultProvider="MyRoleProvider" enabled="true" cacheRolesInCookie="true">
  <providers>
    <clear />
    <add name="MyRoleProvider" type="MyApp.Infrastructure.MyRoleProvider" />
  </providers>
</roleManager>
...

В провайдере, является ли пользователь вашим IPrincipal?

public MyUser User { get; private set; }

Пользователь должен иметь и IIdentity

в MyUser.cs:

    ...
    public virtual ICollection<Role> Roles { get; set; }
    public IIdentity Identity { get; set; }

У меня нет много дополнительных советов, чтобы помочь устранить неполадки из ваших комментариев.

ОБНОВИТЬ

Некоторые примеры, с которыми я ознакомился и которые были полезны при настройке моей: http://www.brianlegg.com/post/2011/05/09/Implementing-your-own-RoleProvider-and-MembershipProvider-in-MVC-3.aspx

http://www.mattwrock.com/post/2009/10/14/Implementing-custom-MembershipProvider-and-Role-Provider-for-Authinticating-ASPNET-MVC-Applications.aspx

http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx?Redirected=true

Я прочитал много других статей и ТАК постов во время моего первого пробежки, но это были вещи, которые я потрудился добавить в закладки. Я использовал подход ролей / прав к авторизации, поэтому один из них ориентирован именно на это.

Чтобы это исправить, вам нужно сделать 4 обновления для вашего приложения.

    1. Create a class that extends RoleProvider.

    namespace MyApp
    {


     public class MyRoleProvider : RoleProvider
        {
            public override string ApplicationName
            {
                get
                {
                    throw new NotImplementedException();
                }

                set
                {
                    throw new NotImplementedException();
                }
            }

            public override void AddUsersToRoles(string[] usernames, string[] roleNames)
            {
                throw new NotImplementedException();
            }

            public override void CreateRole(string roleName)
            {
                throw new NotImplementedException();
            }

            public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
            {
                throw new NotImplementedException();
            }

            public override string[] FindUsersInRole(string roleName, string usernameToMatch)
            {
                throw new NotImplementedException();
            }

            public override string[] GetAllRoles()
            {
                throw new NotImplementedException();
            }

            public override string[] GetRolesForUser(string username)
            {
                using (ApplicationDbContext db = new ApplicationDbContext())
                {
                        // get user roles here using user name.

                }
            }



            public override string[] GetUsersInRole(string roleName)
            {
                throw new NotImplementedException();
            }

            public override bool IsUserInRole(string username, string roleName)
            {

                return GetRolesForUser(username).Contains(roleName);

            }

            public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
            {
                throw new NotImplementedException();
            }

            public override bool RoleExists(string roleName)
            {
                throw new NotImplementedException();
            }
        }

}

    2. Create a custom filter that extends AuthorizeAttribute and overwrite its methods.

      public class MyAuthFilter : AuthorizeAttribute
    {


        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
       }


        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            var routeValues = new RouteValueDictionary(new
            {
                controller = "Account",
                action = "Login",


            });

             filterContext.Result = new RedirectToRouteResult(routeValues);

            base.HandleUnauthorizedRequest(filterContext);
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            string[] roles = Roles.Split(',');

            string userName = HttpContext.Current.User.Identity.Name;

            MyRoleProvider myProvider = new MyRoleProvider();


            foreach (string role in roles)
            {
                bool success = myProvider.IsUserInRole(userName, role);

                if (success == true)
                {
                    return true;
                }

            }

            return false;
        }

    3. Configure your custom role provider in your web.config.
      <system.web>
        <roleManager defaultProvider="MyRoleProvider" enabled="true" cacheRolesInCookie="true">
          <providers>
            <clear />
            <add name="MyRoleProvider" type="MyApp.MyRoleProvider" />
          </providers>
        </roleManager>
      </system.web>

      Note: The type here uses the fully qualified namespace and your class name = MyApp.MyRoleProvider. Yours can be different

    4. Use your custom filter instead of the default Authorize attribute for your controllers and actions. E.g 

    [MyAuthFilter]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";

            return View();
        }
    }
Другие вопросы по тегам