React SPA + .NET CORE + AuthorizationHandler

时间:2019-08-13 19:41:20

标签: .net .net-core azure-active-directory

我的React应用程序位于.net core 2.2之后

我通过AzureAD保护了它

// Sign-in users with the Microsoft identity platform
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("ISINPClient", options));

我添加如下所示的授权策略。我的Auth处理程序会检查用户的声明中是否有与从app.settings加载的内容相对应的组

itemArray = Configuration.GetSection("AllowedGroups").Get<string[]>();
    services.AddMvc(options =>
                {
                    var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .AddRequirements(new GroupAccessRequirement(itemArray))
                        .Build();
                    options.Filters.Add(new AuthorizeFilter(policy));
                }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

这是授权栏

    public class GroupAccessRequirement : AuthorizationHandler<GroupAccessRequirement>, IAuthorizationRequirement
{
    private string[] _groups { get; set; }
    public GroupAccessRequirement(string[] groups)
    {
        _groups = groups;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GroupAccessRequirement requirement)
    {

        if (_groups == null)
            context.Succeed(requirement);


        foreach (var group in _groups)
        {
            if (context.User.HasClaim(claim => claim.Value == group))
            {
                context.Succeed(requirement);
            }
        }

        return Task.CompletedTask;
    }
}

为了强制.net核心通过Azure进行身份验证,我在下面提供了这段代码

app.Use(async (context, next) =>
            {
                if (context.User.Identity.IsAuthenticated == false && context.Request.Path != "/signin-oidc")
                {
                    await context.ChallengeAsync(AzureADDefaults.AuthenticationScheme);
                }
                else
                {
                    await next.Invoke();
                }
            });

因此,在这一点上,我已通过身份验证。

  1. 但是我需要强制调用GroupAccessRequirement以确保用户具有组授权才能进入SPA。

  2. 如果您的用户没有权限显示哪个页面? SPA或.NET访问被拒绝?

问题:如何在显示SPA之前验证AuthorizationHandler以及在SPA或.NET核心中显示哪个访问被拒绝的页面?

谢谢。

1 个答案:

答案 0 :(得分:0)

不确定该问题是否已解决,但我想加两分钱。

  • AddAzureAD最能为您做两件事-将OpenIdConnect添加为挑战方案,将cookie添加为默认方案。未经身份验证的用户将自动定向到OpenIdConnect(AAD身份验证),一旦完成,所有其他客户端/服务器身份验证/授权都将通过Cookie进行。
  • 由于#1,对于您的授权方案,如果不满足您的组策略,则将在授权服务中发生ForbidAsync,并将用户定向到cookie方案中指定的AccessDeniedPath。
  • 由于AddAzureAD无法为您提供配置Cookie方案参数的机会,因此,如果我没有记错的话,AccessDeniedPath将是默认值-/ Account / AccessDenied。

如果您想自定义该行为,则可能必须:

  1. 不要使用AddAzureAD,而是通过AddOpenIdConnect和AddCookie配置自己的OpenIdConnect方案(挑战)和cookie方案(默认)。
  2. 将CookieAuthenticationAuthentications.AccessDeniedPath更改为您自己的自定义页面。
  3. 将您的SPA主页配置为需要授权-例如将其从静态文件移动到Razor页面。

#2之后,您可能需要注意api控制器,因为您想将HTTP 403而不是302返回到拒绝访问页面。 Asp.net核心tries to return 403 for AJAX requests,但根据我的经验,可能还不够。您可以通过设置自己的OnRedirectToAccessDenied事件来自定义该事件-例如如果请求URL以/ api /开头,则返回403。

最后一件事,依靠从id令牌获取组成员身份可能不是一个好主意-如果用户具有许多组成员身份,AAD将发送一个占位符而不是真实的组。最好的选择是请求访问令牌并对照Microsoft Graph API(不是即将死去的AAD Graph)进行检查。但这确实带来了另一个问题,您的应用必须具有Directory.Read.All之类的范围,需要管理员的同意。 :(

我希望有一个更简单的选择。很高兴听到别人的消息。

总而言之,我认为对于SPA应用程序,最好从SPA本身进行身份验证。 Microsoft具有MSAL.js库,可以帮助您解决此问题。它并不完美,但在大多数情况下都可以使用。有很多示例-我也有some code on github在使用它。它还包含一些代码,可将asp.net核心授权策略“传递”给客户端,以供React应用使用,以提供更好的用户体验。随时检查一下。