为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

PHP
HUX布斯 2021-11-14 10:50:38
主要目标是在 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;        }
查看完整描述

2 回答

?
白衣染霜花

TA贡献1796条经验 获得超10个赞

我已经对框架源进行了一些挖掘,并找到了一种以授权处理程序方式进行这项工作的方法。


授权过程的入口点是AuthorizeFilter。过滤器上下文有一个接受IActionResult的Result属性。通过设置此属性,您可以缩短请求并显示您想要的任何操作结果(包括视图)。这是解决问题的关键。


如果遵循执行路径,您会发现过滤器上下文已传递给授权组件,并且在IAuthorizationHandler.HandleRequirementAsync方法中可用。您可以通过向下转换从上下文对象的Resource属性中获取它(如 OP 所示)。


还有一件更重要的事情:您必须从授权处理程序返回成功,否则最终不可避免地会发生重定向。(如果您查看IPolicyEvaluator的默认实现,这将变得清晰。)


所以把这一切放在一起:


public class BlockedHandler : AuthorizationHandler<BlockedRequirement>

{

    private Task HandleBlockedAsync(AuthorizationFilterContext filterContext)

    {

        // create a model for the view if needed...

        var model = new BlockedModel();


        // do some processing if needed...


        var modelMetadataProvider = filterContext.HttpContext.RequestServices.GetService<IModelMetadataProvider>();

        // short-circuit request by setting the action result

        filterContext.Result = new ViewResult

        {

            StatusCode = 403, // Client cannot access the requested resource


            ViewName = "~/Views/Shared/Blocked.cshtml",

            ViewData = new ViewDataDictionary(modelMetadataProvider, filterContext.ModelState) { Model = model }

        };


        return Task.CompletedTask;

    }


    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockedRequirement requirement)

    {

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

            context.Resource is AuthorizationFilterContext filterContext &&

            filterContext.ActionDescriptor is ControllerActionDescriptor descriptor)

        {

            var allowBlocked = descriptor.ControllerTypeInfo.CustomAttributes

                .Concat(descriptor.MethodInfo.CustomAttributes)

                .Any(x => x.AttributeType == typeof(AllowBlockedAttribute));


            if (!allowBlocked)

                await HandleBlockedAsync(filterContext);

        }


        // We must return success in every case to avoid forbid/challenge.

        context.Succeed(requirement);

    }

}


查看完整回答
反对 回复 2021-11-14
?
斯蒂芬大帝

TA贡献1827条经验 获得超8个赞

我认为 AuthorizationHandler 绝对是放置这个逻辑的更好的地方。但是 - 如果我理解正确 - 您的问题是要调用的操作在此处理程序执行时已被选择,因此您无法再更改路由。

当然,标准方法是启动重定向,但您希望避免这种情况以保留当前 URL。

鉴于上述情况,我可以想到一种方法:全局操作过滤器

操作过滤器可以在调用单个操作方法之前和之后立即运行代码。它们可用于操作传递给动作的参数和动作返回的结果。

OnActionExecuting方法似乎是把你的逻辑,因为在这一点上,你可以访问操作方法的属性在正确的地方,你有机会短路的处理(通过设置结果的财产ActionExecutingContext参数)如果用户被阻止。

如果您不熟悉过滤器的概念,您将在这篇 MSDN 文章 中找到所有详细信息。


查看完整回答
反对 回复 2021-11-14
  • 2 回答
  • 0 关注
  • 173 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信