【问题标题】:Creating a AuthorizeAttribute - what do I need to know?创建 AuthorizeAttribute - 我需要知道什么?
【发布时间】:2012-02-08 10:10:01
【问题描述】:

这是我的要求:

  • 我将向 N 个角色添加用户;在数据库中定义。

  • 我需要使用我的授权属性保护每个控制器操作。

例如,Web 应用程序会检查登录的用户是否属于这两个角色中的任何一个,如果是,我让他们进入。我如何告诉 Authorize 属性从我选择的数据库表?

 [Authorize(Roles = "Admin, Technician")]
 public ActionResult Edit(int id)
 {
     return View();
 }

我尝试过用谷歌搜索许多不同的页面,但似乎没有一个适合我需要的内容并且过于复杂。

如果official documentation 有什么我也想找到它,因为我没有看到任何我可以使用的东西。

有什么建议吗?

例如,这个问题的答案看起来很干净,但我不知道它是完整的还是遗漏了一些重要的东西。

ASP.NET MVC3 Role and Permission Management -> With Runtime Permission Assignment


编辑

看来我真正要寻找的是创建一个自定义角色提供程序,对吗?我是否需要来实现这个类并将其用作我的角色提供者?我对此很陌生,有什么想法吗?

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

【问题讨论】:

    标签: asp.net-mvc-3 roles


    【解决方案1】:

    过去几周我经历了几乎相同的情况,所以这可能会帮助同一条船上的其他人。我的场景是公司 Intranet 上的 MVC4 应用程序,用户存储在 Active Directory 中。这允许 Windows 身份验证提供单点登录,因此不需要表单身份验证。角色存储在 Oracle 数据库中。我有 3 个角色:

    • 只读:所有用户都需要成为其中的成员才能访问应用程序
    • 用户:创建新资源
    • 管理员:编辑和删除记录

    我决定使用 asp.net 角色提供程序 api 来创建我自己的 AccountRoleProvider。到目前为止,我只需要使用 2 个方法,GetRolesForUser 和 IsUserInRole:

    public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
    {
        private readonly IAccountRepository _accountRepository;
    
        public AccountRoleProvider(IAccountRepository accountRepository)
        {
            this._accountRepository = accountRepository;
        }
    
        public AccountRoleProvider() : this (new AccountRepository())
        {}
    
        public override string[] GetRolesForUser(string user521)
        {
            var userRoles = this._accountRepository.GetRoles(user521).ToArray();
    
            return userRoles;
        }
    
        public override bool IsUserInRole(string username, string roleName)
        {
            var userRoles = this.GetRolesForUser(username);
    
            return Utils.IndexOfString(userRoles, roleName) >= 0;
        }
    }
    

    我更新了 web.config 以使用我的角色提供者:

    <authentication mode="Windows" />
    <roleManager enabled="true" defaultProvider="AccountRoleProvider">
      <providers>
        <clear/>
        <add name="AccountRoleProvider"
             type="MyApp.Infrastructure.AccountRoleProvider" />
      </providers>
    </roleManager>
    

    然后我从 AuthorizeAttribute、ReadOnlyAuthorize 和 CustomAuthorize 创建了 2 个自定义属性。

    只读授权:

    public class ReadonlyAuthorize : AuthorizeAttribute
    {
        private IAccountRepository _accountRepository;
    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var user = httpContext.User;
            this._accountRepository = new AccountRepository();
    
            if (!user.Identity.IsAuthenticated)
            {
                return false;
            }
    
            // Get roles for current user
            var roles = this._accountRepository.GetRoles(user.Identity.Name);
    
            if (!roles.Contains("readonly"))
            {
                return false;
            }
    
            return base.AuthorizeCore(httpContext);
        }
    
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
    
            if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
            {
                filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
            }
        }
    }
    

    自定义授权:

    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public string RedirectActionName { get; set; }
        public string RedirectControllerName { get; set; }
        private IAccountRepository _accountRepository;
    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var user = httpContext.User;
            this._accountRepository = new AccountRepository();
            var accessAllowed = false;
    
            // Get the roles passed in with the (Roles = "...") on the attribute
            var allowedRoles = this.Roles.Split(',');
    
            if (!user.Identity.IsAuthenticated)
            {
                return false;
            }
    
            // Get roles for current user
            var roles = this._accountRepository.GetRoles(user.Identity.Name);
    
            foreach (var allowedRole in allowedRoles)
            {
                if (roles.Contains(allowedRole))
                {
                    accessAllowed = true;
                }
            }
    
            if (!accessAllowed)
            {
                return false;
            }
    
            return base.AuthorizeCore(httpContext);
        }
    
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
    
            if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
            {   
                var values = new RouteValueDictionary(new
                {
                    action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
                    controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
                });
    
                filterContext.Result = new RedirectToRouteResult(values);
            }
        }
    }
    

    使用 2 个不同属性的原因是,我将一个用于 Readonly 角色,所有用户都必须是其成员才能访问应用程序。我可以在 Global.asax 的 RegisterGlobalFilters 方法中添加它,这意味着它会自动应用于每个控制器:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new ReadonlyAuthorize());
    }
    

    然后在 CustomAuthorize 中,我可以采取更精细的方法并指定我想要的角色并应用于控制器或单个操作,例如下面我可以将删除方法的访问权限限制为管理员角色的用户:

    [AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
    public ActionResult Delete(int id = 0)
    {
        var batch = myDBContext.Batches.Find(id);
        if (batch == null)
        {
            return HttpNotFound();
        }
    
        return View(batch);
    }
    

    我需要采取进一步的步骤,例如使用当前用户所属的角色更新 User 对象。这将在我的自定义属性中检索一次而不是每次都检索用户的角色,并且还利用 User.IsInRole。在 Gloal.asax 的 Application_AuthenticateRequest 中应该可以实现这样的事情:

    var roles = "get roles for this user from respository";
    
    if (Context.User != null)
        Context.User = new GenericPrincipal(Context.User.Identity, roles);
    

    【讨论】:

      【解决方案2】:

      有很多方法可以处理这个问题。 Darin 的方法和吹镖(都是非常熟练的人 - 其中一个也是安全作者)在您提供的链接中很不错。

      需要注意的是缓存。如果您使用服务器端输出缓存缓存,您可能会无意中为一个用户缓存某些内容,然后返回给另一个用户。请看:

      OutputCache and Authorize filters in MVC3

      Why can't I combine [Authorize] and [OutputCache] attributes when using Azure cache (.NET MVC3 app)?

      MVC Custom Authentication, Authorization, and Roles Implementation

      如果您正在使用授权属性,请了解有关此内容以及如何处理缓存的更多信息。

      【讨论】:

        【解决方案3】:

        您可以使用默认的成员资格提供程序和角色提供程序,而不是自己实现,但您还需要使用默认的 asp.net 成员资格数据库 aspnetdb.mdf。

        请参阅here 了解演练

        【讨论】:

          猜你喜欢
          • 2010-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-18
          • 2014-02-01
          • 1970-01-01
          • 2012-05-14
          • 2021-09-19
          相关资源
          最近更新 更多