首页 文章

覆盖ASP.NET Core MVC 1.0中的全局授权过滤器

提问于
浏览
19

我试图在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{}

这当然不会起作用,因为"global filter"不允许 UserManager 访问控制器,因为它们不是"admins" .

在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 角色会发生什么 .

2 回答

  • 1

    您需要稍微使用框架,因为您的全局策略比您想要应用于特定控制器和操作的策略更具限制性:

    • 默认情况下,只有管理员用户可以访问您的应用程序

    • 特定角色也将被授予对某些控制器的访问权限(例如访问 UsersController 的UserManagers)

    正如您已经注意到的那样,拥有全局过滤器意味着只有Admin用户才能访问控制器 . 在 UsersController 上添加其他属性时,只有 both Admin and 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>());
    

    有了这个,默认策略将用于这两个控制器:

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

    此处将使用特定的用户策略:

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

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

  • 31

    在每个调用的上下文中使用@Daniel 's solution I ran into the same issue mentioned by @TarkaDaal in the comment (there' s 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()));
    });
    

    这很难看,但它适用于这种情况,因为如果你仍然需要 new AuthorizationPolicyBuilder().RequireRole("admin").Build() 过滤器来处理它 .

相关问题