本文介绍了在 ASP.NET Core 1.0 MVC 中覆盖全局授权过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to set up authorization in ASP.NET Core 1.0 (MVC 6) web app.

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

More restrictive approach - by default I want to restrict all controllers and action methods to users with Admin role. So, I am adding a global authorize attribute like:

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

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

Then I want to allow users with specific roles to access concrete controllers. For example:

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

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

Which of course will not work, as the "global filter" will not allow the UserManager to access the controller as they are not "admins".

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

In MVC5, I was able to implement this by creating a custom authorize attribute and putting my logic there. Then using this custom attribute as a global. For example:

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 似乎有所不同.

I tried to create a custom AuthorizeFilter, but no success. API seems to be different.

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

So my question is: Is it possible to set up default policy and then override it for specific controllers and actions. Or something similar.I don't want to go with this

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

在每个控制器/操作上,因为这是一个潜在的安全问题.如果我不小心忘记放置 Admin 角色会发生什么.

on every controller/action, as this is a potential security problem. What will happen if I accidentally forget to put the Admin role.

推荐答案

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

You will need to play with the framework a bit since your global policy is more restrictive than the one you want to apply to specific controllers and actions:

  • 默认情况下,只有管理员用户可以访问您的应用程序
  • 特定角色也将被授予对某些控制器的访问权限(例如UserManagers 访问UsersController)
  • By default only Admin users can access your application
  • Specific roles will also be granted access to some controllers (like UserManagers accessing the UsersController)

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

As you have already noticied, having a global filter means that only Admin users will have access to a controller. When you add the additional attribute on the UsersController, only users that are both Admin and UserManager will have access.

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

It is possible to use a similar approach to the MVC 5 one, but it works in a different way.

  • In MVC 6 the [Authorize] attribute does not contain the authorization logic.
  • Instead the AuthorizeFilter is the one that has an OnAuthorizeAsync method calling the authorization service to make sure policies are satisfied.
  • A specific IApplicationModelProvider is used to add an AuthorizeFilter for every controller and action that has an [Authorize] attribute.

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

One option could be to recreate your IsAdminOrAuthorizeAttribute, but this time as an AuthorizeFilter that you will then add as a global filter:

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] 属性时,这才会应用您的全局过滤器.

This would apply your global filter only when the controller/action doesn't have a specific [Authorize] attribute.

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

You could also avoid having a global filter by injecting yourself in the process that generates the filters to be applied for every controller and action. You can either add your own IApplicationModelProvider or your own IApplicationModelConvention. Both will let you add/remove specific controller and actions filters.

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

For example, you can define a default authorization policy and extra specific policies:

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

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

Then you can create a new IApplicatioModelProvider that will add the default policy to every controller that doesn't have its own [Authorize] attribute (An application convention would be very similar and probably more aligned with the way the framework is intended to be extended. I just quickly used the existing AuthorizationApplicationModelProvider as a guide):

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 个控制器上使用默认策略:

With this in place, the default policy will be used on these 2 controllers:

public class FooController : Controller

[Authorize]
public class BarController : Controller

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

And the specific Users policy will be used here:

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

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

Notice that you still need to add the admin role to every policy, but at least all your policies will be declared in a single startup method. You could probably create your own methods for building policies that will always add the admin role.

这篇关于在 ASP.NET Core 1.0 MVC 中覆盖全局授权过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 12:31