“无限循环ASP.NET MVC和Azure Active Directory B2C

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

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

我的Startup类看起来像这样:

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

问题是: 当我点击实例化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下的HandleUnauthorizedRequest方法,并按如下方式定义自定义授权属性:

 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" }; } } 

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

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

我试图只允许角色为“全局管理员”的Azure Active Directory B2C用户访问以下类

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

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

注意:要检查用户是否是全局管理员,您可以检索当前用户目录下的角色,然后使用getMemberObjects API检索当前用户所属的组,角色,然后检查全局管理员角色id在当前用户的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 

更新:

我查看了我的实施。 以下是检查当前登录用户角色的代码。

 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")); } 

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

这是我修改后的自定义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); } } 

按如下方式装饰UserProfileController

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