【问题标题】:Is it possible to use custom filters based on AuthorizeAttribute in a Razor view?是否可以在 Razor 视图中使用基于 AuthorizeAttribute 的自定义过滤器?
【发布时间】:2016-08-15 19:59:00
【问题描述】:

是否可以在剃刀视图中使用自定义过滤器?

例如,我有这个在控制器中工作:

[Privilege(Privileges ="AdminRead, AdminWrite"))]
public ActionResult Index()
{
return View();
}

但是是否有可能在 Razor cshtml 文件中执行以下操作:

if(@[Privilege(Privileges ="AdminRead, AdminWrite"))])
{
//html goes here
}

如果有所不同,则 PrivilegeAttribute 派生自 AuthorizeAttribute。

PrivilegeAttribute.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace IdentityDevelopment.Infrastructure
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class PrivilegeAttribute : AuthorizeAttribute
    {
        private static readonly string[] _emptyArray = new string[0];


        private string _privileges;
        private string[] _privilegesSplit = _emptyArray;

        public string Privileges
        {
            get { return _privileges ?? String.Empty; }
            set
            {
                _privileges = value;
                _privilegesSplit = SplitString(value);
            }
        }

        internal static string[] SplitString(string original)
        {
            if (String.IsNullOrEmpty(original))
            {
                return _emptyArray;
            }

            var split = from piece in original.Split(',')
                        let trimmed = piece.Trim()
                        where !String.IsNullOrEmpty(trimmed)
                        select trimmed;
            return split.ToArray();
        }

      public PrivilegeAttribute(string privilegeList)
    {
        _privileges = privilegeList;
    }
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            bool isAuthorized = base.AuthorizeCore(httpContext);

            if (isAuthorized) {
                string[] rolesArray;

                var roles = ((ClaimsIdentity)httpContext.User.Identity).Claims
                    .Where(c => c.Type == ClaimTypes.Role)
                    .Select(c => c.Value);

                rolesArray = roles.ToArray();

                //Assume that a user can only be associated to 0 or 1 role. If 0 the rolesArray will be null.

                if (rolesArray != null)
                {
                    string roleUser = rolesArray[0];

                    SQLRolerecord CheckPrivInRole = new SQLRolerecord();

                    return CheckPrivInRole.Allow(roleUser, _privilegesSplit);

                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }


        }

    }
}

谢谢。

【问题讨论】:

  • 这不可能吗? @if (User.IsInRole("WhateverUserRole"))
  • @if(User.IsAuthorized)应该这样做
  • @techspider 是的,我已经使用了它,这是可能的,但是自定义 AuthorizeAttributes 呢?例如,我有一个名为 PrivilegeAttribute 的函数,它接受一个名为“Privileges”的输入,那么我如何能够对此做类似的事情呢?如何创建一个名为 IsInPrivilege 的方法?
  • 属性或过滤器不是视图而是控制器方法;您可以使用自定义 HTML 辅助方法来完成此操作
  • @jbutler483 我更新了我的问题,重点关注基于 AuthorizeAttribute 的自定义过滤器。

标签: html asp.net asp.net-mvc razor


【解决方案1】:

我相信你的问题的答案已经很好地封装了here

您可以根据基于角色的安全性来限制用户查看。

@if (User.IsInRole("Admin"))
{
   //here blocks that you want to show to users with Admin role   
}

您也可以通过控制器处理授权,例如。

public ActionResult Index()
{
    if(User.IsInRole("Admin"))
    {
        return View("Admin");
    }
    return View("User");
}

但是您特别想要 Authorize 属性,例如。

[Authorize(Roles = "Admin")]
public ActionResult SaveTopSecret()
{

}

【讨论】:

  • 我想你可能在我编辑的时候写了这个答案。我知道如何使用 User.IsInRole 执行此操作,但我想知道是否可以使用自定义类型的授权来完成,这就是我在原始问题中编辑的内容。
【解决方案2】:

向视图添加属性或过滤器不是首选。您可以在this MSDN article 进行战利品。

过滤器可以应用于操作方法、控制器或 应用层。

您也可以通过在您的视图中选中User.IsInRole 并执行所需的操作来实现相同的目的。

另外,您还可以实现自定义 HTML 帮助程序,它可以像 HTML 控件上的扩展方法一样发挥作用。您可以在问题here 中查看示例。

【讨论】:

    【解决方案3】:

    理论上,由于ViewContext 扩展了ControllerContext,您可以实例化该属性并像这样调用它:

    @if(new PrivilegeAttribute{Privileges = "AdminRead, AdminWrite"}
            .AuthorizeCore(new AuthorizationContext(this.ViewContext)))
    {
       ...
    }
    

    这与向视图“添加属性”不同,但它允许您在视图中重用属性的自定义逻辑,以避免重复代码。

    但是,我应该注意,首选完成您正在做的事情的方法是:

    1. 把逻辑放到一个单独的类中
    2. 将该类(或其接口)注入到您的控制器中
    3. 让控制器操作调用该类/接口的方法来发现用户是否有权限。
    4. 让控制器操作将该信息保存到强类型视图模型中,并将视图模型传递到视图中
    5. 让视图访问 view-model 属性以确定如何显示内容。

    【讨论】:

    • 有趣的提议!!你试过这个吗?是否可以使其等同于[Privilege(Privileges ="AdminRead, AdminWrite"))]
    • @techspider:我自己没有尝试过,但我想不出任何不应该工作的原因。请参阅我的更新答案,了解如何使其与您发布的内容相同。
    • 我会查看您发布的首选方法,但是当我尝试使用您建议的 Razor 代码方法时,我看到错误消息 The type or namespace name 'PrivilegeAttribute could not be found (are you missing a using directive or an assembly reference?) 我的视图不是强类型到任何模型此刻。
    • @ITWorker 将 namespaceTheNamespaceOfYourPrivelegeAttribute 添加到该视图;
    • @StriplingWarrior 我最终还是实现了首选方法,尽管我使用 viewbag 而不是模型绑定。
    【解决方案4】:

    好的,我已经了解了使用枚举设置权限的 AuthorizeAttribute 路线。使用字符串来定义权限似乎更清晰一点,也少了一点。

    所以这在控制器中工作正常

     [AccessRole(AccessLevel.SuperAdmin, AccessLevel.Admin)]
    

    例如。现在我们必须解决剃刀问题。 第 1 步将此添加到您的 AccessRole 类中

     public bool Auth(HttpContextBase httpContext)
            {
                return this.AuthorizeCore(httpContext);
            }
    

    第2步添加这个类(访问级别是枚举)

    public static class AccessRoleHelper {
    
    
            public static bool IsInRole(HttpContextBase httpContext, params AccessLevel[] roles)
        {
    
            var ac = new AccessRole(roles);
    
            return ac.Auth(httpContext);
    
        }
    
    
    
        }
    

    步骤 3. 将命名空间添加到 razor web.cong

    第 4 步。

    @if (AccessRoleHelper.IsInRole(this.Context, AccessLevel.Admin, AccessLevel.SuperAdmin))
        <p>has access</p>
    }
    

    嗯,我还在学习 c# - 但我总是问自己,我对这个解决方案满意吗?时间会证明一切。但是一个解决方案也不少。

    [编辑] 您可以通过在 razor 中添加更多包含命名空间来进行一些清理。

    【讨论】:

    • 当我读到你的答案时,我最终实现了这个版本。感谢您的回复。
    【解决方案5】:

    Attribute 是...

    摘录:

    一种将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联的强大方法。属性与程序实体关联后,可以在运行时使用一种称为反射的技术来查询该属性。

    所以你的代码:

    if(@[Privilege(Privileges ="AdminRead, AdminWrite"))])
    {
      //html goes here
    }
    

    完全无效,因为属性只能与类型、方法或属性相关联。 (又名...)

    [MyClassAttribute]
    public class MyClass
    {
      [MyPropertyAttribute]
      public int Height { get; set; }
    
      [MyMethodAttribute]
      public int GetWidth()
      { 
        //.....
      }
    }
    

    听起来您想封装和重用您的代码。如果是这种情况,那么您需要删除自定义属性中的大部分代码并将其放在其他位置。例如,您可以:

    public static class IPrincipleExtensions
    {
      public static bool HasAccess(this IPrinciple principle, IEnumerable<string> roles)
      {
      }
    }
    

    此属性适用于控制器

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, 
     Inherited = true, 
     AllowMultiple = true)]
    public class PrivilegeAttribute : AuthorizeAttribute
    {
      protected override bool AuthorizeCore(HttpContextBase httpContext)
      {
        bool isAuthorized = base.AuthorizeCore(httpContext);
    
        if (isAuthorized)
        {
          httpContext.Identity.HasAccess(_privilegesSplit)
        }
    
        return isAuthorized;
    }
    

    您可以在视图中重用 PrivilegeProvider

    // something like
    @if(User.HasAccess("AdminRead, AdminWrite"))
    {
      //html goes here
    }
    

    虽然以这种方式使用它实际上只是对 User.IsInRole 的一种破解,所以它需要更多的代码来做同样的事情。

    【讨论】:

    • 感谢您的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 1970-01-01
    相关资源
    最近更新 更多