`[Authorize(Roles = "admin")]` 无限循环 ASP.NET MVC 和 Azure Active Directory B2C [英] `[Authorize(Roles = "admin")]` Infinite loop ASP.NET MVC and Azure Active Directory B2C

查看:22
本文介绍了`[Authorize(Roles = "admin")]` 无限循环 ASP.NET MVC 和 Azure Active Directory B2C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图仅允许具有全局管理员"角色的 Azure Active Directory B2C 用户访问以下类(这就是我包含 Authorize 命令的原因):

[Authorize(Roles = "admin")]公共类 UserProfileController :控制器{... 控制器类 ...}

我的 Startup 类如下所示:

公共部分类启动{私有静态字符串 clientId = ConfigurationManager.AppSettings["ida:ClientId"];私有静态字符串 appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];私有静态字符串 aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];私有静态字符串tenantId = ConfigurationManager.AppSettings["ida:TenantId"];私有静态字符串 postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];//这是 AAD Graph API 的资源 ID.我们需要它来请求令牌来调用 Graph API.私有静态字符串graphResourceId = "https://graph.microsoft.com";私有静态只读字符串权限= aadInstance + 租户ID;公共静态 GraphServiceClient graphClient = null;公共静态 GraphServiceClient GetGraphServiceClient(){返回图客户端;}public void ConfigureAuth(IAppBuilder app){app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);app.UseCookieAuthentication(new CookieAuthenticationOptions(){CookieSecure = CookieSecureOption.Always});app.UseOpenIdConnectAuthentication(新的 OpenIdConnectAuthenticationOptions{客户端 ID = 客户端 ID,权威=权威,PostLogoutRedirectUri = postLogoutRedirectUri,通知 = 新 OpenIdConnectAuthenticationNotifications(){//如果 OpenID Connect 响应中有代码,则将其兑换为访问令牌和刷新令牌,并将它们存储起来.AuthorizationCodeReceived = (上下文) =>{变量代码 = 上下文.代码;ClientCredential 凭证 = 新 ClientCredential(clientId, appKey);string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;TokenCache userTokenCache = new ADALTokenCache(signedInUserID);AuthenticationContext authContext = new AuthenticationContext(Authority, userTokenCache);AuthenticationResult 结果 = authContext.AcquireTokenByAuthorizationCode(代码,新 Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),凭据,graphResourceId);字符串令牌 = result.AccessToken;尝试{图客户端 = 新图服务客户端(新的 DelegateAuthenticationProvider((requestMessage) =>{requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);返回 Task.FromResult(0);}));}捕获(例外 e){System.Diagnostics.Debug.WriteLine("创建图形客户端失败:" + e.Message);}返回 Task.FromResult(0);}}});}}

问题是:当我点击实例化 UserProfileController 的按钮时,AuthorizationCodeReceived = (context) => 代码行里面的代码被一次又一次地调用处于无限循环中. 如何修复无限循环,以便只有 Azure Active Directory B2C全局管理员"可以实例化 UserProfileController?

解决方案

[授权(角色 =管理员")]

由于您使用 Authorize 属性来检查用户的角色,因此您需要确保当前用户的声明具有有效的角色声明.您可以利用以下代码片段来检查您当前的用户声明:

return Json((User.Identity as ClaimsIdentity).Claims.Select(c => new { key = c.Type, value = c.Value }),JsonRequestBehavior.AllowGet);

<块引用>

问题是:当我点击实例化 UserProfileController 的按钮时,AuthorizationCodeReceived = (context) => 这行代码在无限循环中被一次又一次地调用.

您可以覆盖 AuthorizeAttribute 并定义您的自定义授权属性如下:

公共类 MyAuthorize : AuthorizeAttribute{受保护的覆盖无效 HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext){filterContext.Result = new ContentResult() { Content = "您无权采取行动" };}}

然后,您可以按如下方式装饰您的 UserProfileController 控制器:

[MyAuthorize(Roles = "admin")]公共类 UserProfileController :控制器{//去做:}

<块引用>

我试图仅允许具有全局管理员"角色的 Azure Active Directory B2C 用户访问以下类

AuthorizationCodeReceived委托方法下,检索访问令牌后,需要利用Microsoft Graph客户端库来检查当前用户是否为全局管理员/公司管理员与否.如果当前用户是全局管理员/公司管理员,则需要指定角色声明如下:

context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "admin"));

注意:要检查用户是否为全局管理员,您可以检索当前用户目录下的角色,然后使用 getMemberObjects API 来检索当前用户所属的组和角色,然后检查全局管理员角色 ID 是否在当前用户的 MemberObjects 中.

//列出目录角色,https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/directoryrole_listvar roles=await graphClient.DirectoryRoles.Request().GetAsync();//用户:getMemberObjects,https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_getmemberobjects

更新:

我检查了我这边的实现.这是检查当前登录用户的角色的代码.

var directoryRoles = await graphClient.DirectoryRoles.Request().GetAsync();var userRoles = await graphClient.Me.MemberOf.Request().GetAsync();var adminRole=directoryRoles.Where(role => role.DisplayName== "公司管理员" || role.DisplayName == "全局管理员").FirstOrDefault();if (userRoles.Count(role => role.Id == adminRole.Id) > 0){context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "admin"));}别的{context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "user"));}

注意:要添加多个用户角色,您可以添加多个 new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "") 角色索赔.

这是我修改后的自定义AuthorizeAttribute:

公共类 MyAuthorize : AuthorizeAttribute{private bool noPermission = false;公共字符串权限{获取;放;}protected override bool AuthorizeCore(HttpContextBase httpContext){如果 (!base.AuthorizeCore(httpContext))返回假;var permissionArrs = Permissions.Trim().Split('|');if (permissionArrs.ToList().Exists(p=>httpContext.User.IsInRole(p))){返回真;}别的{noPermission = true;返回假;}}受保护的覆盖无效 HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext){如果(无权限)filterContext.Result = new ContentResult() { Content = "您无权采取行动" };别的base.HandleUnauthorizedRequest(filterContext);}}

如下装饰UserProfileController:

[MyAuthorize(Permissions = "admin|co-admin")]公共类 UsersController :控制器{//去做:}

I am attempting to allow only Azure Active Directory B2C users with role "Global Administrator" to access the following class (which is why I have included the Authorize command):

[Authorize(Roles = "admin")]
public class UserProfileController : Controller
{
    ... controller class ...
}

And my Startup class looks like this:

public partial class Startup
{
    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
    private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
    // This is the resource ID of the AAD Graph API.  We'll need this to request a token to call the Graph API.
    private static string graphResourceId = "https://graph.microsoft.com";
    private static readonly string Authority = aadInstance + tenantId;
    public static GraphServiceClient graphClient = null;

    public static GraphServiceClient GetGraphServiceClient()
    {
        return graphClient;
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            CookieSecure = CookieSecureOption.Always
        });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = Authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
                   AuthorizationCodeReceived = (context) => 
                   {
                       var code = context.Code;
                       ClientCredential credential = new ClientCredential(clientId, appKey);
                       string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                       TokenCache userTokenCache = new ADALTokenCache(signedInUserID);

                       AuthenticationContext authContext = new AuthenticationContext(Authority, userTokenCache);
                       AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                           code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);

                       string token = result.AccessToken;

                       try
                       {
                           graphClient = new GraphServiceClient(
                               new DelegateAuthenticationProvider(
                                   (requestMessage) =>
                                   {
                                       requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);

                                       return Task.FromResult(0);
                                   }));
                       }
                       catch (Exception e)
                       {
                           System.Diagnostics.Debug.WriteLine("Failed to create graph client: " + e.Message);
                       }

                       return Task.FromResult(0);
                   }
                }
            });
    }
}

The problem is: when I click on the button that instantiates the UserProfileController, then the the code inside of AuthorizationCodeReceived = (context) => line of code is called again and again in an infinite loop. How can I fix the infinite loop so that only Azure Active Directory B2C "Global Administrators" can instantiate the UserProfileController?

解决方案

[Authorize(Roles = "admin")]

Since your are using the Authorize attribute to check user's roles, you need to make sure the current user's Claims have the valid role claim. You could leverage the following code snippet to check your current user claims:

return Json((User.Identity as ClaimsIdentity).Claims.Select(c => new { key = c.Type, value = c.Value }),JsonRequestBehavior.AllowGet);

The problem is: when I click on the button that instantiates the UserProfileController, then the the code inside of AuthorizationCodeReceived = (context) => line of code is called again and again in an infinite loop.

You could override the HandleUnauthorizedRequest method under AuthorizeAttribute and define your custom authorize attribute as follows:

public class MyAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        filterContext.Result = new ContentResult() { Content = "You don't have rights to take actions" };
    }
}

Then, you could decorate your UserProfileController controller as follows:

[MyAuthorize(Roles = "admin")]
public class UserProfileController : Controller
{
    //TODO:
}

I am attempting to allow only Azure Active Directory B2C users with role "Global Administrator" to access the following class

Under the AuthorizationCodeReceived delegate method, after you retrieve the access token, you need to leverage the Microsoft Graph Client library to check whether the current user is a Global Administrator / Company Administrator or not. If the current user is a Global Administrator / Company Administrator, then you need to specify the role claim as follows:

context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "admin"));

Note: For checking whether a user is a Global Administrator, you could retrieve the roles under the current user's directory, then use the getMemberObjects API to retrieve the groups, roles that the current user is a member of, then check whether the Global Administrator role id is in current user's MemberObjects.

//List directory roles, https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/directoryrole_list
var roles=await graphClient.DirectoryRoles.Request().GetAsync();

//user: getMemberObjects ,https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_getmemberobjects

UPDATE:

I checked the implementation on my side. Here is the code to check the role for the current logged user.

var directoryRoles = await graphClient.DirectoryRoles.Request().GetAsync();
var userRoles = await graphClient.Me.MemberOf.Request().GetAsync();

var adminRole=directoryRoles.Where(role => role.DisplayName== "Company Administrator" || role.DisplayName == "Global Administrator").FirstOrDefault();
if (userRoles.Count(role => role.Id == adminRole.Id) > 0)
{
    context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "admin"));
}
else
{
    context.AuthenticationTicket.Identity.AddClaim(new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "user"));
}

Note: For adding multiple user roles, you could add multiple new Claim(context.AuthenticationTicket.Identity.RoleClaimType, "<role-name>") role claims.

Here is my modified custom AuthorizeAttribute:

public class MyAuthorize : AuthorizeAttribute
{
    private bool noPermission = false;

    public string Permissions { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!base.AuthorizeCore(httpContext))
            return false;

        var permissionArrs = Permissions.Trim().Split('|');

        if (permissionArrs.ToList().Exists(p=>httpContext.User.IsInRole(p)))
        {
            return true;
        }
        else
        {
            noPermission = true;
            return false;
        }
    }

    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (noPermission)
            filterContext.Result = new ContentResult() { Content = "You don't have rights to take actions" };
        else
            base.HandleUnauthorizedRequest(filterContext);
    }
}

Decorate the UserProfileController as follows:

[MyAuthorize(Permissions = "admin|co-admin")]
public class UsersController : Controller
{
   //TODO:
}

这篇关于`[Authorize(Roles = "admin")]` 无限循环 ASP.NET MVC 和 Azure Active Directory B2C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆