gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 23:44:09 28 4
gpt4 key购买 nike

我正在使用自定义授权属性过滤器向 ASP.NET Core 3.1 应用程序添加 AzureAD 身份验证(并最终授权)。下面的代码实现了 IAuthorizationFilterOnAuthorization我将用户重定向到 SignIn 的方法当他们的身份验证到期时页面。
当 Controller Action 带有 [CustomAuthorizationFilter]命中我希望属性的 OnAuthorization无论身份验证 cookie 是否已过期,都会立即命中的方法。
这种期望不会发生,相反,如果用户未通过身份验证并且触发了 Controller 操作,则用户会自动通过 Microsoft 重新进行身份验证并创建一个有效的 cookie,然后才会出现 OnAuthorization方法被击中,击败我认为是OnAuthorization的目的方法。
我一直在做很多研究来理解这种行为,但我显然错过了一些东西。我发现的最有用的信息是在 Microsoft docs :

As of ASP.NET Core 3.0, MVC doesn't add AllowAnonymousFilters for[AllowAnonymous] attributes that were discovered on controllers andaction methods. This change is addressed locally for derivatives ofAuthorizeAttribute, but it's a breaking change forIAsyncAuthorizationFilter and IAuthorizationFilter implementations.


因此,似乎使用 IAuthorizationFilter 的实现可能在 3.0+ 中坏了,我不知道如何修复它。
这种行为是正常的还是我的实现不正确?
如果正常,为什么我在 OnAuthorization之前重新认证方法运行?
如果不正确,我该如何正确实现?
自定义授权过滤器.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 filter 来解决这个问题,但由于时间限制,我决定使用常规操作过滤器。它适用于 AJAX 调用,如果用户未经授权或未经身份验证,它会将用户重定向到适当的页面:
AjaxAuthorize Action 过滤器:

//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();
}
});
}

关于c# - 自定义授权过滤器在 ASP.NET Core 3 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64017688/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com