自定义授权过滤器在 ASP.NET Core 3 中不起作用

2024-03-26

我正在努力使用自定义授权属性过滤器将 AzureAD 身份验证(以及最终授权)添加到 ASP.NET Core 3.1 应用程序。下面的代码实现了IAuthorizationFilter's OnAuthorization我将用户重定向到的方法SignIn身份验证过期时的页面。

当控制器动作时[CustomAuthorizationFilter]被击中我期望属性的OnAuthorization无论身份验证 cookie 是否已过期,都会立即调用该方法。

这种期望不会发生,相反,如果用户未经过身份验证并且点击了控制器操作,则系统会自动通过 Microsoft 重新对用户进行身份验证,并创建一个有效的 cookie,只有这样,OnAuthorization方法被击中,击败了我认为的目的OnAuthorization method.

我做了很多研究来理解这种行为,但我显然遗漏了一些东西。我发现的最有用的信息是微软文档 https://learn.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.0#authorization-iallowanonymous-removed-from-authorizationfiltercontextfilters:

从 ASP.NET Core 3.0 开始,MVC 不添加AllowAnonymousFilters 在控制器上发现的 [AllowAnonymous] 属性和 行动方法。此更改已在本地针对以下衍生品进行解决 AuthorizeAttribute,但这是一个重大变化 IAsyncAuthorizationFilter 和 IAuthorizationFilter 实现。

所以,看来实现与IAuthorizationFilter在 3.0+ 中可能会被破坏,我不知道如何修复它。

这种行为正常还是我的实施不正确?

如果正常的话为什么我要在之前重新验证OnAuthorization方法运行?

如果不正确,我该如何正确实施?

自定义授权Filter.cs

public class CustomAuthorizationFilter : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        string signInPageUrl = "/UserAccess/SignIn";

        if (context.HttpContext.User.Identity.IsAuthenticated == false)
        {
            if (context.HttpContext.Request.IsAjaxRequest())
            {
                context.HttpContext.Response.StatusCode = 401;
                JsonResult jsonResult = new JsonResult(new { redirectUrl = signInPageUrl });
                context.Result = jsonResult;
            }
            else
            {
                context.Result = new RedirectResult(signInPageUrl);
            }
        }
    }
}

使用的 IsAjaxRequest() 扩展:

//Needed code equivalent of Request.IsAjaxRequest().
//Found this solution for ASP.NET Core: https://stackoverflow.com/questions/29282190/where-is-request-isajaxrequest-in-asp-net-core-mvc
//This is the one used in ASP.NET MVC 5: https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Mvc/AjaxRequestExtensions.cs
public static class AjaxRequestExtensions
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        if (request.Headers != null)
        {
            return (request.Headers["X-Requested-With"] == "XMLHttpRequest");
        }

        return false;
    }
}

Startup.cs 中的 AzureAD 身份验证实现

public void ConfigureServices(IServiceCollection services)
{
    IAppSettings appSettings = new AppSettings();
    Configuration.Bind("AppSettings", appSettings);

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options =>
        {
            options.Instance = appSettings.Authentication.Instance;
            options.Domain = appSettings.Authentication.Domain;
            options.TenantId = appSettings.Authentication.TenantId;
            options.ClientId = appSettings.Authentication.ClientId;
            options.CallbackPath = appSettings.Authentication.CallbackPath;
        });

    services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
    {
        options.UseTokenLifetime = false;
        options.Authority = options.Authority + "/v2.0/"; //Microsoft identity platform       
        options.TokenValidationParameters.ValidateIssuer = true;
        // https://stackoverflow.com/questions/49469979/azure-ad-b2c-user-identity-name-is-null-but-user-identity-m-instance-claims9
        // https://stackoverflow.com/questions/54444747/user-identity-name-is-null-after-federated-azure-ad-login-with-aspnetcore-2-2
        options.TokenValidationParameters.NameClaimType = "name";
        //https://stackoverflow.com/a/53918948/12300287
        options.Events.OnSignedOutCallbackRedirect = context =>
        {
            context.Response.Redirect("/UserAccess/LogoutSuccess");
            context.HandleResponse();

            return Task.CompletedTask;
        };
    });

    services.Configure<CookieAuthenticationOptions>(AzureADDefaults.CookieScheme, options =>
    {
        options.AccessDeniedPath = "/UserAccess/NotAuthorized";
        options.LogoutPath = "/UserAccess/Logout";
        options.ExpireTimeSpan = TimeSpan.FromMinutes(appSettings.Authentication.TimeoutInMinutes);
        options.SlidingExpiration = true;
    });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles();

    app.UseRouting();
        
    app.UseAuthentication(); // who are you?            
    app.UseAuthorization(); // are you allowed?

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=UserAccess}/{action=Login}/{id?}");
    });
}

我希望找到一种方法来创建一个AuthorizeAttribute过滤器来解决这个问题,但由于时间限制,我选择了常规操作过滤器。它与 AJAX 调用配合使用,如果用户未经授权或未经身份验证,它会将用户重定向到适当的页面:

Ajax 授权操作过滤器:

//custom AjaxAuthorize filter inherits from ActionFilterAttribute because there is an issue with 
//a inheriting from AuthorizeAttribute.
//post about issue: 
//https://stackoverflow.com/questions/64017688/custom-authorization-filter-not-working-in-asp-net-core-3

//The statuses for AJAX calls are handled in InitializeGlobalAjaxEventHandlers JS function.

//While this filter was made to be used on actions that are called by AJAX, it can also handle
//authorization not called through AJAX.
//When using this filter always place it above any others as it is not guaranteed to run first.

//usage: [AjaxAuthorize(new[] {"RoleName", "AnotherRoleName"})]
public class AjaxAuthorize : ActionFilterAttribute
{
    public string[] Roles { get; set; }

    public AjaxAuthorize(params string[] roles)
    {
        Roles = roles;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        string signInPageUrl = "/UserAccess/SignIn";
        string notAuthorizedUrl = "/UserAccess/NotAuthorized";

        if (context.HttpContext.User.Identity.IsAuthenticated)
        {
            if (Roles.Length > 0)
            {
                bool userHasRole = false;
                foreach (var item in Roles)
                {
                    if (context.HttpContext.User.IsInRole(item))
                    {
                        userHasRole = true;
                    }
                }
                if (userHasRole == false)
                {
                    if (context.HttpContext.Request.IsAjaxRequest())
                    {
                        context.HttpContext.Response.StatusCode = 401;
                        JsonResult jsonResult = new JsonResult(new { redirectUrl = notAuthorizedUrl });
                        context.Result = jsonResult;
                    }

                    else
                    {
                        context.Result = new RedirectResult(notAuthorizedUrl);
                    }
                }
            }

        }
        else
        {
            if (context.HttpContext.Request.IsAjaxRequest())
            {
                context.HttpContext.Response.StatusCode = 403;
                JsonResult jsonResult = new JsonResult(new { redirectUrl = signInPageUrl });
                context.Result = jsonResult;
            }
            else
            {
                context.Result = new RedirectResult(signInPageUrl);
            }
        }
    }
}

使用的 IsAjaxRequest() 扩展(重新发布以获得完整答案):

//Needed code equivalent of Request.IsAjaxRequest().
//Found this solution for ASP.NET Core: https://stackoverflow.com/questions/29282190/where-is-request-isajaxrequest-in-asp-net-core-mvc
//This is the one used in ASP.NET MVC 5: https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Mvc/AjaxRequestExtensions.cs
public static class AjaxRequestExtensions
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        if (request.Headers != null)
        {
            return (request.Headers["X-Requested-With"] == "XMLHttpRequest");
        }

        return false;
    }
}

JavaScript ajax 全局错误处理程序:

//global settings for the AJAX error handler. All AJAX error events are routed to this function.
function InitializeGlobalAjaxEventHandlers() {
    $(document).ajaxError(function (event, xhr, ajaxSettings, thrownError) {
        //these statuses are set in the [AjaxAuthorize] action filter
        if (xhr.status == 401 || xhr.status == 403) {
            var response = $.parseJSON(xhr.responseText);
            window.location.replace(response.redirectUrl);
        } else {
           RedirectUserToErrorPage();
        }     
    });
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义授权过滤器在 ASP.NET Core 3 中不起作用 的相关文章

  • 如何在之前的 Facebook 身份验证后自动安全地让用户登录?

    用户抱怨他们必须过于频繁地登录 如果身份验证完全基于 Facebook OAuth 那么用户如何在下次访问该页面时自动登录 用户流程示例 用户点击 使用 Facebook 登录 用户通过 Facebook 进行身份验证并被重定向回网站 用户
  • 防止 boost::asio::io_context 在空轮询调用时停止

    此代码调用发布的句柄 boost asio io context ioc boost asio post ioc std cout lt lt lol lt lt std endl ioc poll 而这并没有 boost asio io
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • ASP.Net Core 内容配置附件/内联

    我正在从 WebAPI 控制器返回一个文件 Content Disposition 标头值自动设置为 附件 例如 处置 附件 文件名 30956 pdf 文件名 UTF 8 30956 pdf 当它设置为附件时 浏览器将要求保存文件而不是打
  • 如何在 C# 控制台应用程序中将修饰符(ctrl、alt、shift)按键捕获为单个按键?

    Console ReadKey 仅在按下 正常 键时捕获输入 然后将修饰符 如果有 附加为键信息的一部分 如何将单个修饰键注册为输入 提供了一种解决方案这个链接 https blogs msdn microsoft com toub 200
  • 类的成员复制

    在学习 复制成员 概念时 书中给出了如下说法 此外 如果非静态成员是引用 const 或没有复制赋值的用户定义类型 则无法生成默认赋值 我不太明白这个声明到底想传达什么 或者说这个说法指的是哪一种场景 谢谢 该语句与编译器自动为您编写的类
  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • 为什么 set_symmetry_difference 无法与比较器一起使用?

    Example program include
  • C++ php 和静态库

    我创建了一个library a 其中包含 cpp 和 h 文件 其中包含很多类 嵌套类和方法 我想在 php 示例中包含这个静态库并尝试使用它 我想提一下 我是 php 新手 我已经在 test cpp 文件中测试了我的 libray a
  • 如何在标准 WPF ListView 中启用 UI 虚拟化

    我正在使用 NET 4 5 VS2012 并且我有一个 ListView 看起来像这样
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • ASP.NET MailMessage.BodyEncoding 和 MailMessage.SubjectEncoding 默认值

    很简单的问题 但我在 MSDN 上找不到答案 查找 ASP NET 将用于的默认值 MailMessage BodyEncoding and MailMessage SubjectEncoding 如果你不在代码中设置它们 Thanks F
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • MVC4 捆绑:由于 css3 功能而缩小失败?

    我想使用 MVC4 捆绑和缩小 但我总是在未缩小的 css 文件中收到此错误消息作为注释 Minification failed Returning unminified contents 534 29 run time error CSS
  • C++ Streambuf 方法可以抛出异常吗?

    我正在尝试找到一种方法来获取读取或写入流的字符数 即使存在错误并且读 写结束时间较短 该方法也是可靠的 我正在做这样的事情 return stream rdbuf gt sputn buffer buffer size 但如果streamb
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • 将 char[][] 转换为 char** 会导致段错误吗?

    好吧 我的 C 有点生疏了 但我想我应该用 C 来做我的下一个 小 项目 这样我就可以对其进行抛光 并且我已经有不到 20 行的段错误了 这是我的完整代码 define ROWS 4 define COLS 4 char main map
  • C++0x中disable_if在哪里?

    Boost 两者都有enable if and disable if 但 C 0x 似乎缺少后者 为什么它被排除在外 C 0x 中是否有元编程工具允许我构建disable if按照enable if 哦 我刚刚注意到std enable i

随机推荐

  • 匿名委托中捕获的私有字段

    class A public event EventHandler AEvent class B private A foo private int bar public void AttachToAEvent foo AEvent del
  • 数据工厂子项修改或创建日期

    我有一个数据工厂 V2 管道 由 获取元数据 和 forEach 活动组成 用于读取文件共享 本地 上的文件列表并将其记录在数据库表中 目前 我只能读取文件名 但还想检索每个文件的修改日期和 或创建日期属性 有什么帮助吗 谢谢 Accord
  • 使用SQL计算时间序列中的时间间隔

    我有一个像这样的 MySQL 表 CREATE TABLE IF NOT EXISTS vals DT datetime NOT NULL value INT 11 NOT NULL PRIMARY KEY DT DT 是唯一的日期和时间
  • iOS 自定义字体已移位

    对于一个项目我买了一个OTF字体并将其包含到我的项目中 添加到 plist 文件等 它有效 我可以在按钮上设置字体 但标签会被替换 我添加了屏幕截图 以便您可以看到我的问题 有人知道问题出在哪里吗 系统 iPad 3 iOS 6 0 bet
  • 有什么办法可以提取底层Xaml吗?

    无论如何 是否可以从控件中提取底层 xaml IE 我有一个名为 fooBox 的文本框 我可以在运行时从文本框获取代表文本框的 xaml 吗 这向您展示了完整的生命周期 从控件到 XAML 再到控件 如你看到的 string s Xaml
  • 有保证的方法可以在运行时获取成员字段的源顺序吗?

    我正在寻找一种按源顺序检索 在运行时 类的字段的方法 以便我可以执行自己的 初始化处理 该处理基于声明的顺序 我知道 Javadoc 用于Class getDeclaredFields 明确指出不保证订单 SO 上的一些答案指向Javass
  • iOS7中选择单元格时UITableView分隔线消失

    在我的 tableView 中 我在单元格之间设置了分隔线 我允许选择多个单元格 这是我设置选定单元格背景颜色的代码 UIView cellBackgroundColorView UIView alloc initWithFrame cel
  • 如何从没有 jenkinsfile 的存储库触发 Jenkins 管道构建?

    我有一个不包含詹金斯文件 https jenkins io doc book pipeline jenkinsfile 而且我无法影响存储库本身 这意味着我既不能向存储库添加也不能更改任何文件 在本例中 它是Qt repo http cod
  • ArrayAdapter 中 textview 的自定义字体

    我正在尝试更改 a 的字体TextView in my ArrayAdapter 字体chantelli antiqua ttf位于资产文件夹中 这是我的 Java 代码 listItemAdapter new ArrayAdapter
  • 按字母顺序对数组进行排序

    我有一个数组 我需要按出现次数对其元素进行排序 然后按字母顺序排序 例如 55 The 32 ASomething 32 BSomething ASomething should come before Bsomething because
  • 在iOS中以编程方式生成ppt

    为了生成 PDF 文件 我们可以使用 Core Graphics 同样 是否有任何框架 类可用于通过代码生成 ppt 幻灯片演示 文件 或者是否有任何第三方框架用于此目的 没有内置的 Apple API 我也从未听说过有这样的第三方框架
  • 将 sybase 中现有的 int 列更改为标识

    赛贝斯12 5 我有一个正在生产中的现有表 需要更改它的 PK int 列 以便自动填充 创建表时 理想情况下将 ID 列创建为标识 此 ID 列是多个其他表中的外键 因此不能删除该表并重新开始 问题是 我无法将 PK 设置为 IDENTI
  • 如何使面板内的鼠标拖动从窗口移开?

    我想启用这个 System Windows Forms Panel 以便如果用户单击并拖动鼠标 则将窗口拖动到周围 我可以这样做吗 我必须实施多个事件吗 最适合我的解决方案是使用非托管代码 与 HatSoft 发布的答案不同 它可以为您提供
  • MySQL:如何实现行级事务锁定而不是表锁定

    这是用例 我有一张桌子 上面有一堆可用或不可用的唯一代码 作为事务的一部分 我想选择表中可用的代码 然后在事务中更新该行 由于这种情况可能会同时在许多会话中同时发生 因此我希望理想地选择一个随机记录并在表上使用行级锁定 以便其他事务不会被从
  • 将美国州名映射到字典中分别给出的两个字母首字母缩略词

    假设现在我有一个dataframe有 2 列 州和城市 然后我有一个单独的dict每个州的两个字母的缩写 现在我想添加第三列来映射州名称及其两个字母的缩写 我应该在 Python Pandas 中做什么 例如示例问题如下 import pa
  • Swift 2.0,SpriteKit - 滚动视图不使用页面。

    好的 我有一个滚动视图 它已被子类化 可以应用于我在这里问的上一个问题中的任何场景 SpriteKit Swift 2 0 反向滚动视图 https stackoverflow com questions 34712427 spriteki
  • 自定义 Json 封送处理

    我有一个第三方 json api 可以在 go 中使用 它有一些以键值形式返回数据的端点 例如 这里是状态的 json result 0 done 1 incomplete 2 completed 正如您所看到的 它不是一个数组 而是一个对
  • 解释 1 位位域的行为

    include
  • Kubernetes - 部署初始化 - 如何确保它只发生一次?

    我使用 Kubernetes 1 12 我有一个服务 例如 pod 它可能有多个实例 例如副本 gt 1 我的目标是执行维护任务 例如创建 升级数据库 生成证书等 before任何服务实例均已启动 我正在考虑使用 Init Containe
  • 自定义授权过滤器在 ASP.NET Core 3 中不起作用

    我正在努力使用自定义授权属性过滤器将 AzureAD 身份验证 以及最终授权 添加到 ASP NET Core 3 1 应用程序 下面的代码实现了IAuthorizationFilter s OnAuthorization我将用户重定向到的