【问题标题】:Dotnet Core 2.2 - Azure AD Auth Work but Roles Base Returns Access DeniedDotnet Core 2.2 - Azure AD 身份验证工作但角色库返回访问被拒绝
【发布时间】:2019-02-27 15:13:45
【问题描述】:

我正在尝试让角色在 .Net Core 2.2 中工作,但其他解决方案均无效。

Startup.cs 中,Microsoft 在新的 .Net 2.2 中生成此代码,由于某种原因该代码无法正常工作,但让该代码块工作并不是主题,尽管很高兴知道它为什么不工作.说“没有指定 authenticationScheme,也没有找到 DefaultChallengeScheme。”但它是 Microsoft 生成的。

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
                .AddAzureAd(options => Configuration.Bind("AzureAd", options));

在我真实的Startup.cs 中,我不得不使用下面的内容

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

所以以上是我让 Azure AD 使用 [Authorize] 属性的唯一方法。问题是当我尝试使用角色授权时。我已经尝试了很多建议,但都没有成功。每当我有 [Authorize(Roles="")] 时,我都会被重定向到 Microsoft 为 Azure AD 生成的 AccountController 中的 AccessDenied() 方法,基本上是拒绝访问。我正在使用我在 AD 中创建的角色,也在 Azure AD 中创建了一个组,还使用了一个名为“域用户”的角色,它基本上授予公司中的每个员工,是每个员工拥有的最基本的授权.如果“域用户”被“拒绝访问”,那么我不知道我在这里不理解什么。

我已关注 Microsoft 文档的角色,但没有任何内容表明我需要在 services.AddAuthentication() 选项中添加更多内容。

【问题讨论】:

    标签: c# azure asp.net-core .net-core azure-active-directory


    【解决方案1】:

    这对我有用,.Net Core 2.2 Web App using Roles。

    编辑应用程序清单以包含角色,例如

    "appRoles": [
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "description": "Admins have the power.",
                "displayName": "Admin",
                "id": "282fc418-cf3a-4a3a-89f8-6500c64695ff",
                "isEnabled": true,
                "lang": null,
                "origin": "Application",
                "value": "Admin"
            },
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "description": "Writers have the ability to edit app data.",
                "displayName": "Writer",
                "id": "3aa1a322-2918-4005-8cc3-51cba010ccc0",
                "isEnabled": true,
                "lang": null,
                "origin": "Application",
                "value": "Writer"
            },
            {
                "allowedMemberTypes": [
                    "User"
                ],
                "description": "Readers have the ability to read app data.",
                "displayName": "Reader",
                "id": "239f93af-4cc0-4d0e-ad04-bda1f8ac2a91",
                "isEnabled": true,
                "lang": null,
                "origin": "Application",
                "value": "Reader"
            }
        ]
    

    Startup.cs

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

    在“顶级代码块”中添加扩展以解决您提到的问题。

    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();
            return builder;
        }
    
        private class ConfigureAzureOptions : IConfigureNamedOptions<OpenIdConnectOptions>
        {
            private readonly AzureAdOptions _azureOptions;
    
            public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }
    
            public void Configure(string name, OpenIdConnectOptions options)
            {
                options.ResponseType = "token id_token";
                options.Resource = _azureOptions.TargetApiAppId;
                options.SaveTokens = true;
                options.ClientId = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
                options.UseTokenLifetime = true;
                options.CallbackPath = _azureOptions.CallbackPath;
                options.RequireHttpsMetadata = false;
            }
    
            public void Configure(OpenIdConnectOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }
    

    AzureADOptions.cs

    public class AzureAdOptions
    {
        public string ClientId { get; set; }
        public string ClientSecret { get; set; }
        public string Instance { get; set; }
        public string Domain { get; set; }
        public string TenantId { get; set; }
        public string CallbackPath { get; set; }
    
        //manually added
        public string TargetApiAppId { get; set; }
    }
    

    在企业应用刀片中为用户分配角色:

    装饰你的控制器:

    [Authorize(Roles = "Writer")]
    

    AccountController.cs

    public class AccountController : Controller
    {
        [HttpGet]
        public IActionResult SignIn()
        {
            var redirectUrl = Url.Action(nameof(HomeController.Index), "Home");
            return Challenge(
                new AuthenticationProperties { RedirectUri = redirectUrl },
                OpenIdConnectDefaults.AuthenticationScheme);
        }
    
        [HttpGet]
        public IActionResult SignOut()
        {
            var callbackUrl = Url.Action(nameof(SignedOut), "Account", values: null, protocol: Request.Scheme);
            return SignOut(
                new AuthenticationProperties { RedirectUri = callbackUrl },
                CookieAuthenticationDefaults.AuthenticationScheme,
                OpenIdConnectDefaults.AuthenticationScheme);
        }
    
        [HttpGet]
        public IActionResult SignedOut()
        {
            if (User.Identity.IsAuthenticated)
            {
                // Redirect to home page if the user is authenticated.
                return RedirectToAction(nameof(HomeController.Index), "Home");
            }
    
            return View();
        }
    
        [HttpGet]
        public IActionResult AccessDenied()
        {
            return View();
        }
    }
    

    【讨论】:

    • 这也是我发现的。必须使用应用清单中定义的应用角色。不过很酷的是,可以在我们的常规本地 AD 中创建一个角色,该角色将显示在 Azure AD 中。可以使用应用清单中创建的角色之一将该角色分配给应用程序。然后,每次将用户添加到本地 AD 时,它会在同步到 Azure AD 后自动获得访问权限。
    • 确实很酷!我在另一个 Web 应用程序中这样做,将它与基于策略的身份验证结合起来,它变得非常强大。例如。 services.AddAuthorization(options =&gt; { options.AddPolicy("Analyst", policy =&gt; policy.RequireRole("DataAnalyst")); options.AddPolicy("Reader", policy =&gt; policy.RequireAssertion(context =&gt; context.User.HasClaim(ClaimTypes.Role, "DataAnalyst") || context.User.HasClaim(ClaimTypes.Role, "DataReader"))); });
    【解决方案2】:

    您需要在 AzureAD 中配置应用程序清单以接收 AD 角色。

    将清单中的“groupMembershipClaims”值更改为“All”。

    请注意,如果您有大量组,您最终可能会使响应过大,因此所有人都可能希望将其缩减为您真正想要流动的那些组。

    【讨论】:

    • 我把它改成了“all”,但我没有得到任何不同的东西。没有角色或组。您将如何削减到您想要的值?我无法指定我想要的角色类型,比如无效值。我仍然没有得到它们。
    • 此时我只能指向 AzureAD 文档,github.com/Azure-Samples/…
    • 这是 .Net Core 的吗?
    • 是的,不幸的是,这是针对 .Net 的,我们正在寻找不同的 .Net Core 解决方案。
    • 流动组仍应与标志一起发生,这不是 .net 版本特定的。添加断点并查看用户是否包含角色声明。如果不是,则可能是 AAD 配置问题。
    【解决方案3】:

    请先确认你得到了token中的groups,将manifest中的“groupMembershipClaims”值改为“All”后,你可以在controller中放入如下代码:

     var claims = User.Claims;
    

    用户在应用程序中通过身份验证后,您应该获得groups 声明:

    然后您可以将属性与命名策略一起使用,然后在启动时定义策略以要求组声明并设置允许的组 ID:

    services.AddAuthorization(options =>
    {
        options.AddPolicy(
            "CanAccessGroup",
            policyBuilder => policyBuilder.RequireClaim("groups", "0c71eab2-6618-4c53-bcce-806xxxxxx"));
    });
    

    在控制器中:

    [Authorize(Policy = "CanAccessGroup")]
    public IActionResult About()
    {
        return View();
    }
    

    如果组 ID 不在用户组声明中,则访问将被拒绝。

    说“没有指定 authenticationScheme,也没有找到 DefaultChallengeScheme。”但它是 Microsoft 生成的。

    如果在VS2017中使用默认的Work or School Accounts模板,安装的Microsoft.AspNetCore.Authentication.AzureAD.UI包版本将是v2.2.0,生成的代码是:

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options => Configuration.Bind("AzureAd", options));
    

    【讨论】:

    • 我想这是迄今为止我见过的最好的方式。我希望让[Authorize(Roles=""] 属性正常工作,我们不能用组来做吗?当我使用他们在模板中生成的services.AddAuthentication 代码时,这就是我得到的。
    • 这是有效的,但问题是我得到了 100 个组。减少它的唯一选择是使用值“安全组”,这让我仍然停留在 100 秒。我想没有其他方法可以减少它。但是请求/响应太大,显示Bad Request
    • @DanielJackson ,那么您不应该使用 groups ,使用 roles ,您可以在应用清单中定义您的应用中的角色,然后将用户分配给他们。一旦你做了这两件事,你得到的 JWT 中就会有一个名为“roles”的声明,其中包含用户的角色。
    • 是的,你是对的,影响过滤需要一段时间,但我现在在代码中获得了应用程序角色,[Authorize(Roles="role")] 正在工作!感谢您的帮助。
    猜你喜欢
    • 2021-09-17
    • 2019-12-13
    • 2016-03-15
    • 2019-05-17
    • 2017-11-28
    • 2018-08-18
    • 1970-01-01
    • 2019-12-12
    • 2018-01-06
    相关资源
    最近更新 更多