【问题标题】:Override global authorize filter in ASP.NET Core 1.0 MVC在 ASP.NET Core 1.0 MVC 中覆盖全局授权过滤器
【发布时间】:2016-03-06 09:05:45
【问题描述】:

我正在尝试在 ASP.NET Core 1.0 (MVC 6) Web 应用中设置授权。

更严格的方法 - 默认情况下,我想将所有控制器和操作方法限制为具有 Admin 角色的用户。因此,我添加了一个全局授权属性,例如:

AuthorizationPolicy requireAdminRole = new AuthorizationPolicyBuilder()
    .RequireAuthenticatedUser()
    .RequireRole("Admin")
    .Build();
services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(requireAdminRole));});

然后我想允许具有特定角色的用户访问具体的控制器。例如:

[Authorize(Roles="Admin,UserManager")]
public class UserControler : Controller{}

这当然行不通,因为“全局过滤器”将不允许 UserManager 访问控制器,因为他们不是“管理员”。

在 MVC5 中,我能够通过创建自定义授权属性并将我的逻辑放在那里来实现这一点。然后将此自定义属性用作全局属性。例如:

public class IsAdminOrAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        ActionDescriptor action = filterContext.ActionDescriptor;
        if (action.IsDefined(typeof(AuthorizeAttribute), true) ||
            action.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), true))
        {
            return;
        }

        base.OnAuthorization(filterContext);
    }
}

我尝试创建自定义AuthorizeFilter,但没有成功。 API 似乎有所不同。

所以我的问题是:是否可以设置默认策略,然后为特定的控制器和操作覆盖它。或者类似的东西。 我不想和这个一起去

[Authorize(Roles="Admin,[OtherRoles]")]

在每个控制器/操作上,因为这是一个潜在的安全问题。如果我不小心忘记输入Admin 角色会怎样。

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc asp.net-core-1.0


    【解决方案1】:

    您需要稍微使用一下该框架,因为您的全局策略比您要应用于特定控制器和操作的策略更严格:

    • 默认情况下,只有 Admin 用户可以访问您的应用程序
    • 还将授予特定角色访问某些控制器的权限(例如 UserManagers 访问 UsersController

    正如您已经注意到的,拥有全局过滤器意味着只有 Admin 用户可以访问控制器。当您在 UsersController 上添加附加属性时,只有 both Admin UserManager 的用户将拥有访问。

    可以使用与 MVC 5 类似的方法,但它的工作方式不同。

    • 在 MVC 6 中,[Authorize] 属性不包含授权逻辑。
    • 取而代之的是AuthorizeFilter 具有OnAuthorizeAsync 方法调用授权服务以确保满足策略的方法。
    • 特定的IApplicationModelProvider 用于为每个具有[Authorize] 属性的控制器和操作添加AuthorizeFilter

    一种选择可能是重新创建您的 IsAdminOrAuthorizeAttribute,但这次是 AuthorizeFilter,然后您将添加为全局过滤器:

    public class IsAdminOrAuthorizeFilter : AuthorizeFilter
    {
        public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
        {
        }
    
        public override Task OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context)
        {
            // If there is another authorize filter, do nothing
            if (context.Filters.Any(item => item is IAsyncAuthorizationFilter && item != this))
            {
                return Task.FromResult(0);
            }
    
            //Otherwise apply this policy
            return base.OnAuthorizationAsync(context);
        }        
    }
    
    services.AddMvc(opts => 
    {
        opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
    });
    

    仅当控制器/操作没有特定的[Authorize] 属性时,才会应用您的全局过滤器。


    您还可以通过将自己注入到生成要应用于每个控制器和操作的过滤器的过程中来避免使用全局过滤器。您可以添加自己的IApplicationModelProvider 或自己的IApplicationModelConvention。两者都可以让您添加/删除特定的控制器和操作过滤器。

    例如,您可以定义默认授权策略和额外的特定策略:

    services.AddAuthorization(opts =>
    {
        opts.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole("admin").Build();
        opts.AddPolicy("Users", policy => policy.RequireAuthenticatedUser().RequireRole("admin", "users"));
    });
    

    然后您可以创建一个新的IApplicatioModelProvider,它将默认策略添加到没有自己的[Authorize] 属性的每个控制器(应用程序约定将非常相似,并且可能更符合框架的方式打算扩展。我只是快速使用现有的AuthorizationApplicationModelProvider作为指导):

    public class OverridableDefaultAuthorizationApplicationModelProvider : IApplicationModelProvider
    {
        private readonly AuthorizationOptions _authorizationOptions;
    
        public OverridableDefaultAuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor)
        {
            _authorizationOptions = authorizationOptionsAccessor.Value;
        }
    
        public int Order
        {
            //It will be executed after AuthorizationApplicationModelProvider, which has order -990
            get { return 0; }
        }
    
        public void OnProvidersExecuted(ApplicationModelProviderContext context)
        {
            foreach (var controllerModel in context.Result.Controllers)
            {
                if (controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().FirstOrDefault() == null)
                {
                    //default policy only used when there is no authorize filter in the controller
                    controllerModel.Filters.Add(new AuthorizeFilter(_authorizationOptions.DefaultPolicy));
                }
            }
        }
    
        public void OnProvidersExecuting(ApplicationModelProviderContext context)
        {            
            //empty    
        }
    }
    
    //Register in Startup.ConfigureServices
    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IApplicationModelProvider, OverridableDefaultAuthorizationApplicationModelProvider>());
    

    有了这个,默认策略将在这 2 个控制器上使用:

    public class FooController : Controller
    
    [Authorize]
    public class BarController : Controller
    

    这里将使用特定的用户策略:

    [Authorize(Policy = "Users")]
    public class UsersController : Controller
    

    请注意,您仍然需要为每个策略添加管理员角色,但至少您的所有策略都将在一个启动方法中声明。您可能可以创建自己的方法来构建始终添加管理员角色的策略。

    【讨论】:

    • 哈,谢谢你的详细解释。正是我想要达到的目标。我知道我可以覆盖AuthorizeFilter,但只是没有付出足够的努力来做到这一点。对于IApplicationModelProvider,我不知道。刚刚实现了IApplicationModelProvider 方法并且可以确认它正在工作。我认为它更符合新的 MVC 身份验证模型的精神,所以我会继续使用它。
    • 我也会尝试IApplicationModelConvention,甚至可以选择在services.AddMvc() 扩展方法中添加它们。关于代码的主要区别是您需要以某种方式将身份验证选项传递给它。
    • 这对我不起作用。我正在尝试 Filter 方法,在我的 OnAuthorizationAsync 方法中,上下文中总是有两个其他过滤器,因此 IsAdminOrAuthorized 过滤器永远不会运行。即使在我的应用程序中没有任何策略/AuthorizeAttributes。
    • 谢谢,这个答案对我解决其他一些问题很有帮助;)
    【解决方案2】:

    使用@Daniel 的解决方案,我遇到了@TarkaDaal 在评论中提到的相同问题(每个呼叫的上下文中有2 个AuthorizeFilter...不太确定它们来自哪里)。

    所以我的解决方法如下:

    public class IsAdminOrAuthorizeFilter : AuthorizeFilter
    {
        public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
        {
        }
    
        public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
        {
            if (context.Filters.Any(f =>
            {
                var filter = f as AuthorizeFilter;
                //There's 2 default Authorize filter in the context for some reason...so we need to filter out the empty ones
                return filter?.AuthorizeData != null && filter.AuthorizeData.Any() && f != this;
            }))
            {
                return Task.FromResult(0);
            }
    
            //Otherwise apply this policy
            return base.OnAuthorizationAsync(context);
        }        
    }
    
    services.AddMvc(opts => 
    {
        opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
    });
    

    这很难看,但它在这种情况下有效,因为如果您只使用不带参数的 Authorize 属性,那么无论如何您都将由 new AuthorizationPolicyBuilder().RequireRole("admin").Build() 过滤器处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-02
      相关资源
      最近更新 更多