如何在授权过程(策略或中间件)中检查自定义属性?

主要目标是在 OIDC 用户具有类型为“BlockedFrom”的自定义声明(在 ClaimsTransformation 中添加)时阻止访问门户。


我已经通过Startup.Configure方法中的中间件解决了它。一般原因是保留原始请求 URL 而不重定向到 /Account/AccessDenied 页面。


app.Use((context, next) =>

{

    var user = context.User;


    if (user.IsAuthenticated())

    {

        // Do not rewrite path when it marked with custom [AllowBlockedAttribute]!

        // /Home/Logout, for example. But how?

        //

        if (user.HasClaim(x => x.Type == UserClaimTypes.BlockedFrom))

        {

            // Rewrite to run specific method of HomeController for blocked users

            // with detailed message.

            //

            context.Request.Path = GenericPaths.Blocked;

        }

    }


    return next();

});

但是有一个意想不到的结果:Logout方法也HomeController被阻塞了。用户被阻止时无法注销,哈哈!想到的第一件事 - 检查自定义属性,例如[AllowBlockedAttribute]. 中间件中的硬编码路径常量看起来很疯狂。如何访问中间件中调用方法的属性?


另一种(更优雅)的方法是将此逻辑自定义BlockedHandler : AuthorizationHandler<BlockedRequirement>并在Startup.ConfigureServices方法的MVC 选项中将其分配为一般策略:


services.AddSingleton<IAuthorizationHandler, BlockedHandler>();


services.AddMvc(options =>

{

    var policy = new AuthorizationPolicyBuilder()

        .RequireAuthenticatedUser()

        .AddRequirements(new BlockedRequirement())

        .Build();


    // Set the default authentication policy to require users to be authenticated.

    //

    options.Filters.Add(new AuthorizeFilter(policy));


}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

假设实现BlockedHandler:


public class BlockedHandler : AuthorizationHandler<BlockedRequirement>

{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockedRequirement requirement)

    {

        if (!context.User.HasClaim(c => c.Type == UserClaimTypes.BlockedFrom))

        {

            context.Succeed(requirement);

            return Task.CompletedTask;

        }



HUX布斯
浏览 155回答 2
2回答

白衣染霜花

我已经对框架源进行了一些挖掘,并找到了一种以授权处理程序方式进行这项工作的方法。授权过程的入口点是AuthorizeFilter。过滤器上下文有一个接受IActionResult的Result属性。通过设置此属性,您可以缩短请求并显示您想要的任何操作结果(包括视图)。这是解决问题的关键。如果遵循执行路径,您会发现过滤器上下文已传递给授权组件,并且在IAuthorizationHandler.HandleRequirementAsync方法中可用。您可以通过向下转换从上下文对象的Resource属性中获取它(如 OP 所示)。还有一件更重要的事情:您必须从授权处理程序返回成功,否则最终不可避免地会发生重定向。(如果您查看IPolicyEvaluator的默认实现,这将变得清晰。)所以把这一切放在一起:public class BlockedHandler : AuthorizationHandler<BlockedRequirement>{&nbsp; &nbsp; private Task HandleBlockedAsync(AuthorizationFilterContext filterContext)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // create a model for the view if needed...&nbsp; &nbsp; &nbsp; &nbsp; var model = new BlockedModel();&nbsp; &nbsp; &nbsp; &nbsp; // do some processing if needed...&nbsp; &nbsp; &nbsp; &nbsp; var modelMetadataProvider = filterContext.HttpContext.RequestServices.GetService<IModelMetadataProvider>();&nbsp; &nbsp; &nbsp; &nbsp; // short-circuit request by setting the action result&nbsp; &nbsp; &nbsp; &nbsp; filterContext.Result = new ViewResult&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; StatusCode = 403, // Client cannot access the requested resource&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ViewName = "~/Views/Shared/Blocked.cshtml",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ViewData = new ViewDataDictionary(modelMetadataProvider, filterContext.ModelState) { Model = model }&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; &nbsp; &nbsp; return Task.CompletedTask;&nbsp; &nbsp; }&nbsp; &nbsp; protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockedRequirement requirement)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (context.User.HasClaim(c => c.Type == UserClaimTypes.BlockedFrom) &&&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.Resource is AuthorizationFilterContext filterContext &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filterContext.ActionDescriptor is ControllerActionDescriptor descriptor)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var allowBlocked = descriptor.ControllerTypeInfo.CustomAttributes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Concat(descriptor.MethodInfo.CustomAttributes)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Any(x => x.AttributeType == typeof(AllowBlockedAttribute));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!allowBlocked)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; await HandleBlockedAsync(filterContext);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // We must return success in every case to avoid forbid/challenge.&nbsp; &nbsp; &nbsp; &nbsp; context.Succeed(requirement);&nbsp; &nbsp; }}

斯蒂芬大帝

我认为 AuthorizationHandler 绝对是放置这个逻辑的更好的地方。但是 - 如果我理解正确 - 您的问题是要调用的操作在此处理程序执行时已被选择,因此您无法再更改路由。当然,标准方法是启动重定向,但您希望避免这种情况以保留当前 URL。鉴于上述情况,我可以想到一种方法:全局操作过滤器。操作过滤器可以在调用单个操作方法之前和之后立即运行代码。它们可用于操作传递给动作的参数和动作返回的结果。该OnActionExecuting方法似乎是把你的逻辑,因为在这一点上,你可以访问操作方法的属性在正确的地方,你有机会短路的处理(通过设置结果的财产ActionExecutingContext参数)如果用户被阻止。如果您不熟悉过滤器的概念,您将在这篇 MSDN 文章 中找到所有详细信息。
打开App,查看更多内容
随时随地看视频慕课网APP