【问题标题】:Override the User.IsInRole and [Authorize(Roles = "Admin")] for MVC4 application覆盖 MVC4 应用程序的 User.IsInRole 和 [Authorize(Roles = "Admin")]
【发布时间】:2013-08-23 22:52:25
【问题描述】:

我已经为我的 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) 

所以我尝试以与设置自定义 Provider 相同的方式实现自定义 RolePrincipal 有什么想法可以做到这一点吗?不确定它需要什么构造函数参数。这是我的尝试:

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

}

【问题讨论】:

  • 您扩展 RolePrincipal 的最后一个代码示例正在远离我所做的任何事情。

标签: asp.net-mvc-4 authorization roleprovider isinrole


【解决方案1】:

您只需要覆盖自定义角色提供程序中的 GetRolesForUser 方法,而不是更符合逻辑的 IsUserInRole,因为这是默认实现调用的方法,会执行一些不需要的缓存。

【讨论】:

    【解决方案2】:

    您在 IPrincipal 类中覆盖 IsInRole,我在 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 添加适当的部分到您的 webconfig 中,您应该适合 Authorize 属性。

    更新以响应您的 cmets

    网络配置应如下所示:

    ...
    <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; }
    

    我没有太多其他建议可以帮助您从 cmets 进行故障排除。

    更新

    我在设置我的时候发现了一些有用的例子: 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

    我在第一次浏览时阅读了许多其他文章和 SO 帖子,但这些都是我费心收藏的东西。我采用角色/权限方法进行授权,这就是为什么其中一个采用这种方式的原因。

    【讨论】:

    • 我尝试了上述建议,但收到以下错误:“Project.Data.User”未实现接口成员“System.Security.Principal.IPrincipal.Identity”
    • 我通过点击IPrincipal的末尾并点击ALT + SHIFT + F10来实现界面。现在我得到一个非静态方法需要一个目标错误
    • 我的堆栈跟踪似乎在以下位置: System.Web.Security.RolePrincipal.IsInRole(String role) 我尝试以与设置自定义提供程序相同的方式实现自定义 RolePrincipal 任何想法我怎么能这样做?不确定它需要什么构造函数参数
    • 也许我只需要使用此信息设置会员提供者
    • 您的自定义会员提供程序是您执行此操作的地方 - 而不是自定义角色提供程序
    【解决方案3】:

    要解决此问题,您需要对应用程序进行 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();
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-06
      • 2013-09-06
      • 2019-12-20
      • 2021-01-11
      • 2018-12-11
      • 2020-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多