问题描述
我正在尝试在 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:
- 默认情况下,只有 Admin 用户可以访问您的应用程序
- 还将授予特定角色访问某些控制器的权限(例如 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.
- 在 MVC 6 中,
[Authorize]
属性不包含授权逻辑. - 取而代之的是
AuthorizeFilter
是具有调用授权服务以确保满足策略的OnAuthorizeAsync
方法的方法. - 具体的
IApplicationModelProvider
用于为每个具有[Authorize]
属性的控制器和操作添加AuthorizeFilter
.
- In MVC 6 the
[Authorize]
attribute does not contain the authorization logic. - Instead the
AuthorizeFilter
is the one that has anOnAuthorizeAsync
method calling the authorization service to make sure policies are satisfied. - A specific
IApplicationModelProvider
is used to add anAuthorizeFilter
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 中的全局授权过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!