2

我有一个使用 Azure AD 进行身份验证的 ASP.Net Core MVC Web 应用程序。我刚刚收到一个新要求,强制用户在输入一些敏感信息之前重新进行身份验证(输入这些新信息的按钮调用一个控制器操作,该操作初始化一个新的视图模型并将部分视图返回到引导模式中)。

我已经关注了这篇文章,它为实现这一要求提供了很好的指南。我必须进行一些调整才能使其与 ASP.Net Core 2.0 一起使用,我认为这是正确的,但我的问题如下......

  1. 将资源过滤器装饰“[RequireReauthentication(0)]”添加到我的控制器操作中有效,但是传递值 0 意味着代码永远不会到达过滤器内的 await.next() 命令。如果我将参数值更改为 30,它可以工作,但似乎非常随意。这个值应该是多少?

  2. 当调用返回完整视图的控制器操作时,重新身份验证起作用。但是,当我从将部分返回到引导模式的 ajax 请求中调用操作时,它会在加载模式之前失败

对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此不允许访问源“ https://localhost:44308 ”

这看起来像一个 CORS 问题,但我不知道为什么它会在通过标准 mvc 进程而不是从 jquery 调用时起作用。添加

services.AddCors();

app.UseCors(builder => builder.WithOrigins(" https://login.microsoftonline.com "));

到我的启动文件没有任何区别。这里可能是什么问题?

启动.cs

public void ConfigureServices(IServiceCollection services)
{
    // Ommitted for clarity...

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddAzureAd(options => Configuration.Bind("AzureAd", options))
    .AddCookie();

    services.AddCors();

    // Ommitted for clarity...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Ommitted for clarity...

    app.UseCors(builder => builder.WithOrigins("https://login.microsoftonline.com"));

    app.UseStaticFiles();

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

AzureAdAuthenticationBuilderExtensions.cs

public static class AzureAdAuthenticationBuilderExtensions
{        
    public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
        => builder.AddAzureAd(_ => { });

    public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
    {
        builder.Services.Configure(configureOptions);
        builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
        builder.AddOpenIdConnect(options =>
        {
            options.ClaimActions.Remove("auth_time");
            options.Events = new OpenIdConnectEvents
            {
                OnRedirectToIdentityProvider = RedirectToIdentityProvider
            };
        });
        return builder;
    }

    private static Task RedirectToIdentityProvider(RedirectContext context)
    {
        // Force reauthentication for sensitive data if required
        if (context.ShouldReauthenticate())
        {
            context.ProtocolMessage.MaxAge = "0"; // <time since last authentication or 0>;
        }
        else
        {
            context.Properties.RedirectUri = new PathString("/Account/SignedIn");
        }

        return Task.FromResult(0);
    }

    internal static bool ShouldReauthenticate(this RedirectContext context)
    {
        context.Properties.Items.TryGetValue("reauthenticate", out string reauthenticate);
        bool shouldReauthenticate = false;

        if (reauthenticate != null && !bool.TryParse(reauthenticate, out shouldReauthenticate))
        {
            throw new InvalidOperationException($"'{reauthenticate}' is an invalid boolean value");
        }

        return shouldReauthenticate;
    }

    // Ommitted for clarity...
}

RequireReauthenticationAttribute.cs

public class RequireReauthenticationAttribute : Attribute, IAsyncResourceFilter
{
    private int _timeElapsedSinceLast;
    public RequireReauthenticationAttribute(int timeElapsedSinceLast)
    {
        _timeElapsedSinceLast = timeElapsedSinceLast;
    }
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
    {
        var foundAuthTime = int.TryParse(context.HttpContext.User.FindFirst("auth_time")?.Value, out int authTime);
        var ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

        if (foundAuthTime && ts - authTime < _timeElapsedSinceLast)
        {
            await next();
        }
        else
        {
            var state = new Dictionary<string, string> { { "reauthenticate", "true" } };
            await AuthenticationHttpContextExtensions.ChallengeAsync(context.HttpContext, OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties(state));
        }
    }
}

创建笔记.cs

[HttpGet]
[RequireReauthentication(0)]
public IActionResult CreateNote(int id)
{
    TempData["IsCreate"] = true;
    ViewData["PostAction"] = "CreateNote";
    ViewData["PostRouteId"] = id;
    var model = new NoteViewModel
    {
        ClientId = id
    };
    return PartialView("_Note", model);
}

剃刀视图(片段)

<a asp-controller="Client" asp-action="CreateNote" asp-route-id="@ViewData["ClientId"]" id="client-note-get" data-ajax="true" data-ajax-method="get" data-ajax-update="#client-note-modal-content" data-ajax-mode="replace" data-ajax-success="ShowModal('#client-note-modal', null, null);" data-ajax-failure="AjaxFailure(xhr, status, error, false);"></a>

所有帮助表示赞赏。谢谢

4

1 回答 1

0

CORS 问题不在您的应用程序中。您的 AJAX 调用正在尝试将身份验证重定向到 Azure AD,这将不起作用。

您可以做的是在您的RedirectToIdentityProvider函数中检查请求是否是 AJAX 请求。如果是,让它返回 401 状态码,不重定向。

然后您的客户端 JS 需要检测状态代码,并发出触发身份验证的重定向。

于 2018-05-30T05:43:00.437 回答