覆盖ASP.NET Core MVC 1.0中的全局授权filter
我试图在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{}
这当然是行不通的,因为“全局filter”不允许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
角色,会发生什么。
您需要稍微使用框架,因为您的全局策略比您想要应用于特定控制器和操作的策略更具限制性:
- 默认情况下,仅管理员用户可以访问您的应用
- 特定角色也将被授予对某些控制器的访问权限(例如访问
UsersController
UserManagers )
正如您已经注意到的那样,拥有全局filter意味着只有Admin用户才能访问控制器。 在UsersController
上添加其他属性时,只有Admin 和 UserManager的用户才能访问。
可以使用与MVC 5类似的方法,但它以不同的方式工作。
- 在MVC 6中,
[Authorize]
属性不包含授权逻辑。 - 而是
AuthorizeFilter
是具有OnAuthorizeAsync
方法的方法,该方法调用授权服务以确保满足策略。 - 特定的
IApplicationModelProvider
用于为每个具有[Authorize]
属性的控制器和操作添加AuthorizeFilter
。
一个选项可能是重新创建您的IsAdminOrAuthorizeAttribute
,但这次作为AuthorizeFilter
,您将添加为全局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]
属性时,才会应用全局filter。
您还可以通过在生成要应用于每个控制器和操作的filter的过程中注入自己来避免使用全局filter。 您可以添加自己的IApplicationModelProvider
或您自己的IApplicationModelConvention
。 两者都允许您添加/删除特定的控制器和操作filter。
例如,您可以定义默认授权策略和其他特定策略:
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 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().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());
有了这个,默认策略将用于这两个控制器:
public class FooController : Controller [Authorize] public class BarController : Controller
此处将使用特定的用户策略:
[Authorize(Policy = "Users")] public class UsersController : Controller
请注意,您仍需要将admin角色添加到每个策略,但至少所有策略都将在单个启动方法中声明。 您可以创建自己的方法来构建始终添加管理角色的策略。
使用@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()
处理new AuthorizationPolicyBuilder().RequireRole("admin").Build()
filter无论如何。