asp.net mvc 上的自定义授权属性
Custom Authorize Attribute on asp.net mvc
我有一个使用 Azure AAD 授权的 asp.net mvc 应用程序。该应用基于此 github 示例:
https://github.com/dushyantgill/VipSwapper/tree/master/TrainingPoint
此应用具有自定义授权属性
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext ctx)
{
if (!ctx.HttpContext.User.Identity.IsAuthenticated)
base.HandleUnauthorizedRequest(ctx);
else
{
ctx.Result = new ViewResult { ViewName = "Error", ViewBag = { message = "Unauthorized." } };
ctx.HttpContext.Response.StatusCode = 403;
}
}
}
然而这对我来说似乎很奇怪。
我在控制器上有这样的东西:
public class GlobalAdminController : Controller
{
// GET: GlobalAdmin
[AuthorizeUser(Roles = "admin")]
public ActionResult Index()
{
return View();
}
}
正如您所见,那里使用了自定义属性,但更深入地查看自定义属性的代码。
显然,在 if 和 ELSE 上,请求都未通过身份验证。
现在看看这张截图。
没有意义吧?
http://screencast.com/t/obqXHZJj0iNG
问题是,我应该怎么做才能让用户执行控制器?
更新一:
在我的身份验证流程中,我有以下内容
public void ConfigureAuth(IAppBuilder app)
{
// configure the authentication type & settings
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// configure the OWIN OpenId Connect options
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = SettingsHelper.ClientId,
Authority = SettingsHelper.AzureADAuthority,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// when an auth code is received...
AuthorizationCodeReceived = (context) => {
// get the OpenID Connect code passed from Azure AD on successful auth
string code = context.Code;
// create the app credentials & get reference to the user
ClientCredential creds = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
// use the ADAL to obtain access token & refresh token...
// save those in a persistent store...
EfAdalTokenCache sampleCache = new EfAdalTokenCache(userObjectId);
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, sampleCache);
// obtain access token for the AzureAD graph
Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(code, redirectUri, creds, SettingsHelper.AzureAdGraphResourceId);
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));
// successful auth
return Task.FromResult(0);
},
AuthenticationFailed = (context) => {
context.HandleResponse();
return Task.FromResult(0);
}
},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false
}
});
}
特别检查IsAAADdmin方法调用
/// <summary>
/// The global administrators and user account administrators of the directory are automatically assgined the admin role in the application.
/// This method determines whether the user is a member of the global administrator or user account administrator directory role.
/// RoleTemplateId of Global Administrator role = 62e90394-69f5-4237-9190-012177145e10
/// RoleTemplateId of User Account Administrator role = fe930be7-5e62-47db-91af-98c3a49a38b1
/// </summary>
/// <param name="objectId">The objectId of user or group that currently has access.</param>
/// <returns>String containing the display string for the user or group.</returns>
public static bool IsUserAADAdmin(ClaimsIdentity Identity)
{
string tenantId = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string signedInUserID = Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
string userObjectID = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new EfAdalTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenSilent(
SettingsHelper.AzureAdGraphResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
HttpClient client = new HttpClient();
string doQueryUrl = string.Format("{0}/{1}/users/{2}/memberOf?api-version={3}",
SettingsHelper.AzureAdGraphResourceId, tenantId,
userObjectID, SettingsHelper.GraphAPIVersion);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, doQueryUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = responseContent.ReadAsStringAsync().Result;
var memberOfObjects = (System.Web.Helpers.Json.Decode(responseString)).value;
if (memberOfObjects != null)
foreach (var memberOfObject in memberOfObjects)
if (memberOfObject.objectType == "Role" && (
memberOfObject.roleTemplateId.Equals("62e90394-69f5-4237-9190-012177145e10", StringComparison.InvariantCultureIgnoreCase) ||
memberOfObject.roleTemplateId.Equals("fe930be7-5e62-47db-91af-98c3a49a38b1", StringComparison.InvariantCultureIgnoreCase)))
return true;
}
return false;
}
我 100% 确定用户处于管理员角色,因为当我调试它时 returns 正确并且声明已创建
更新二:
在调试时,我检查了 User.Claims 并且角色 admin 在那里。
所以我不确定每个角色的授权如何与 User.IsInRole
一起使用
角色的声明类型是 <a href="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" rel="nofollow">http://schemas.microsoft.com/ws/2008/06/identity/claims/role</a>
(which you can shortcut to using ClaimTypes.Role
per here)。
我相信你的 ConfigureAuth
class 应该改成这样:
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));
为此:
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
Esteban,您似乎错过了在 ConfigureAuth 实现中设置角色声明类型。请参阅示例的第 55 行:https://github.com/dushyantgill/VipSwapper/blob/master/TrainingPoint/App_Start/Startup.Auth.cs#L55。一旦你这样做 User.IsInRole() 和授权属性将正常工作。
注册自定义授权属性的实现 - ASP.net 有一个错误,它 returns 授权失败的 401 错误(而不是 403)(这使经过身份验证的用户处于无休止的身份验证中与 IdP 循环)。此自定义授权属性修复了该问题。
希望对您有所帮助。
回见。
我有一个使用 Azure AAD 授权的 asp.net mvc 应用程序。该应用基于此 github 示例:
https://github.com/dushyantgill/VipSwapper/tree/master/TrainingPoint
此应用具有自定义授权属性
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext ctx)
{
if (!ctx.HttpContext.User.Identity.IsAuthenticated)
base.HandleUnauthorizedRequest(ctx);
else
{
ctx.Result = new ViewResult { ViewName = "Error", ViewBag = { message = "Unauthorized." } };
ctx.HttpContext.Response.StatusCode = 403;
}
}
}
然而这对我来说似乎很奇怪。
我在控制器上有这样的东西:
public class GlobalAdminController : Controller
{
// GET: GlobalAdmin
[AuthorizeUser(Roles = "admin")]
public ActionResult Index()
{
return View();
}
}
正如您所见,那里使用了自定义属性,但更深入地查看自定义属性的代码。 显然,在 if 和 ELSE 上,请求都未通过身份验证。
现在看看这张截图。
没有意义吧? http://screencast.com/t/obqXHZJj0iNG
问题是,我应该怎么做才能让用户执行控制器?
更新一: 在我的身份验证流程中,我有以下内容
public void ConfigureAuth(IAppBuilder app)
{
// configure the authentication type & settings
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// configure the OWIN OpenId Connect options
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = SettingsHelper.ClientId,
Authority = SettingsHelper.AzureADAuthority,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// when an auth code is received...
AuthorizationCodeReceived = (context) => {
// get the OpenID Connect code passed from Azure AD on successful auth
string code = context.Code;
// create the app credentials & get reference to the user
ClientCredential creds = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
// use the ADAL to obtain access token & refresh token...
// save those in a persistent store...
EfAdalTokenCache sampleCache = new EfAdalTokenCache(userObjectId);
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, sampleCache);
// obtain access token for the AzureAD graph
Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(code, redirectUri, creds, SettingsHelper.AzureAdGraphResourceId);
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));
// successful auth
return Task.FromResult(0);
},
AuthenticationFailed = (context) => {
context.HandleResponse();
return Task.FromResult(0);
}
},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false
}
});
}
特别检查IsAAADdmin方法调用
/// <summary>
/// The global administrators and user account administrators of the directory are automatically assgined the admin role in the application.
/// This method determines whether the user is a member of the global administrator or user account administrator directory role.
/// RoleTemplateId of Global Administrator role = 62e90394-69f5-4237-9190-012177145e10
/// RoleTemplateId of User Account Administrator role = fe930be7-5e62-47db-91af-98c3a49a38b1
/// </summary>
/// <param name="objectId">The objectId of user or group that currently has access.</param>
/// <returns>String containing the display string for the user or group.</returns>
public static bool IsUserAADAdmin(ClaimsIdentity Identity)
{
string tenantId = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string signedInUserID = Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
string userObjectID = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new EfAdalTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenSilent(
SettingsHelper.AzureAdGraphResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
HttpClient client = new HttpClient();
string doQueryUrl = string.Format("{0}/{1}/users/{2}/memberOf?api-version={3}",
SettingsHelper.AzureAdGraphResourceId, tenantId,
userObjectID, SettingsHelper.GraphAPIVersion);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, doQueryUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = responseContent.ReadAsStringAsync().Result;
var memberOfObjects = (System.Web.Helpers.Json.Decode(responseString)).value;
if (memberOfObjects != null)
foreach (var memberOfObject in memberOfObjects)
if (memberOfObject.objectType == "Role" && (
memberOfObject.roleTemplateId.Equals("62e90394-69f5-4237-9190-012177145e10", StringComparison.InvariantCultureIgnoreCase) ||
memberOfObject.roleTemplateId.Equals("fe930be7-5e62-47db-91af-98c3a49a38b1", StringComparison.InvariantCultureIgnoreCase)))
return true;
}
return false;
}
我 100% 确定用户处于管理员角色,因为当我调试它时 returns 正确并且声明已创建
更新二: 在调试时,我检查了 User.Claims 并且角色 admin 在那里。 所以我不确定每个角色的授权如何与 User.IsInRole
一起使用角色的声明类型是 <a href="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" rel="nofollow">http://schemas.microsoft.com/ws/2008/06/identity/claims/role</a>
(which you can shortcut to using ClaimTypes.Role
per here)。
我相信你的 ConfigureAuth
class 应该改成这样:
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));
为此:
if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
Esteban,您似乎错过了在 ConfigureAuth 实现中设置角色声明类型。请参阅示例的第 55 行:https://github.com/dushyantgill/VipSwapper/blob/master/TrainingPoint/App_Start/Startup.Auth.cs#L55。一旦你这样做 User.IsInRole() 和授权属性将正常工作。
注册自定义授权属性的实现 - ASP.net 有一个错误,它 returns 授权失败的 401 错误(而不是 403)(这使经过身份验证的用户处于无休止的身份验证中与 IdP 循环)。此自定义授权属性修复了该问题。
希望对您有所帮助。
回见。