attribute - ASP.NET MVC Custom Authorization
asp net mvc authentication filter (3)
El AuthorizationContext (parámetro a OnAuthorize) proporciona acceso al Controlador, RouteData, HttpContext, etc. Debería poder usarlos en un filtro de autorización personalizado para hacer lo que desee. A continuación se muestra un ejemplo de código de un RoleOrOwnerAttribute derivado de AuthorizeAttribute.
public override void OnAuthorization( AuthorizationContext filterContext )
{
if (filterContext == null)
{
throw new ArgumentNullException( "filterContext" );
}
if (AuthorizeCore( filterContext.HttpContext )) // checks roles/users
{
SetCachePolicy( filterContext );
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
// custom check for global role or ownership
else if (filterContext.HttpContext.User.IsInRole( "SuperUser" ) || IsOwner( filterContext ))
{
SetCachePolicy( filterContext );
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
}
}
// helper method to determine ownership, uses factory to get data context,
// then check the specified route parameter (property on the attribute)
// corresponds to the id of the current user in the database.
private bool IsOwner( AuthorizationContext filterContext )
{
using (IAuditableDataContextWrapper dc = this.ContextFactory.GetDataContextWrapper())
{
int id = -1;
if (filterContext.RouteData.Values.ContainsKey( this.RouteParameter ))
{
id = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] );
}
string userName = filterContext.HttpContext.User.Identity.Name;
return dc.Table<Participant>().Where( p => p.UserName == userName && p.ParticipantID == id ).Any();
}
}
protected void SetCachePolicy( AuthorizationContext filterContext )
{
// ** IMPORTANT **
// Since we''re performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge( new TimeSpan( 0 ) );
cachePolicy.AddValidationCallback( CacheValidateHandler, null /* data */);
}
Tengo una pregunta sobre la autorización personalizada en MVC.
Tengo un sitio al que deseo limitar el acceso a ciertas páginas, dependiendo de su membresía de grupo. Ahora he visto toneladas de ejemplos sobre cómo hacer esto si hay un solo grupo de administradores y un solo grupo de usuarios, por ejemplo, pero no ejemplos de un tercer nivel.
Por ejemplo, solo los usuarios de una compañía pueden ver los pedidos de su propia compañía (y cada compañía tiene sus propios administradores, etc.). Estas empresas se almacenan en un DB. Así que he visto formas de hacer una autorización personalizada, anulando el método AuthorizeCore
en el AuthorizeAttribute
, pero no sé cómo acceder a los parámetros pasados al controlador para ver si el usuario tiene acceso al pedido (ID de pedido, por ejemplo, ).
¿Es este incluso el mejor lugar para realizar la verificación, o debería ser manejado directamente desde el método del controlador?
Mi respuesta no es buena, porque mata las pruebas unitarias, pero estoy extrayendo valores de System.Web.HttpContext.Current.Session
. El singleton está disponible a lo largo del proyecto. Al guardar el usuario actual en la sesión, puede acceder a él desde cualquier lugar, incluidas las clases de utilidad como AuthorizeAttribute
.
Sin embargo, me encantaría ver una solución comprobable por unidad.
Si la autorización es realmente tan dinámica, la manejaría en el controlador. Tengo una acción en la que hago esto: puede devolver un HttpUnauthoredResultResult para redirigir a la página de inicio de sesión o puede mostrar un error personalizado en su vista.
No hago el redireccionamiento predeterminado a la página de inicio de sesión cuando alguien ya ha iniciado sesión, pero no en el rol correcto. Eso es muy confuso para el usuario.