您需要对框架进行一些调整,因为您的全局策略比要应用于特定控制器和操作的策略更加严格:
- 默认情况下,只有 管理员 用户可以访问您的应用程序
- 某些角色也将被授予访问某些控制器的权限(例如 UserManagers 访问
UsersController
)
正如您已经注意到的那样,具有全局过滤器意味着只有
管理员 用户才能访问控制器。当您在
UsersController
上添加附加属性时,只有同时是
管理员 和
UserManager 的用户才能访问。
可以使用类似于 MVC 5 的方法,但其工作方式不同。
一个选项是重新创建你的IsAdminOrAuthorizeAttribute
,但这次作为一个AuthorizeFilter
,然后将其添加为全局过滤器:
public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context)
{
if (context.Filters.Any(item => item is IAsyncAuthorizationFilter && item != this))
{
return Task.FromResult(0);
}
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"));
});
然后您可以创建一个新的,将默认策略添加到每个没有自己的
[Authorize]
属性的控制器中(应用程序约定非常相似,可能更符合框架扩展的方式。我只是快速使用现有的
AuthorizationApplicationModelProvider
作为指南):
public class OverridableDefaultAuthorizationApplicationModelProvider : IApplicationModelProvider
{
private readonly AuthorizationOptions _authorizationOptions;
public OverridableDefaultAuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor)
{
_authorizationOptions = authorizationOptionsAccessor.Value;
}
public int Order
{
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
foreach (var controllerModel in context.Result.Controllers)
{
if (controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().FirstOrDefault() == null)
{
controllerModel.Filters.Add(new AuthorizeFilter(_authorizationOptions.DefaultPolicy));
}
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
}
}
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, OverridableDefaultAuthorizationApplicationModelProvider>());
有了这个设置,这两个控制器将使用默认策略:
public class FooController : Controller
[Authorize]
public class BarController : Controller
这里将使用具体的用户策略:
[Authorize(Policy = "Users")]
public class UsersController : Controller
请注意,您仍需要将管理员角色添加到每个策略中,但至少所有策略都将在单个启动方法中声明。您可能可以创建自己的方法来构建策略,这将始终添加管理员角色。
AuthorizeFilter
,但只是没有付出足够的努力去做。对于IApplicationModelProvider
,我不知道它的存在。刚刚实现了IApplicationModelProvider
的方法,可以确认它正在工作。我认为这更符合新的 MVC 认证模型的精神,所以我会采用它。 - regnauldIApplicationModelConvention
,甚至可以在services.AddMvc()
扩展方法中添加选项。关于代码的主要区别是您需要将身份验证选项传递给它。 - Daniel J.G.