Autorización basada en usuario (C#)
por Scott Mitchell
Nota:
Desde que se escribió este artículo, los proveedores de pertenencia de ASP.NET han sido reemplazados por ASP.NET Identity. Se recomienda encarecidamente actualizar las aplicaciones para usar la plataforma ASP.NET Identity en lugar de los proveedores de pertenencia destacados en el momento en el que se escribió este artículo. ASP.NET Identity ofrece una serie de ventajas frente al sistema de pertenencia de ASP.NET, incluidas las siguientes:
- Mejor rendimiento
- Extensibilidad y capacidad de prueba mejoradas
- Compatibilidad con OAuth, OpenID Connect y autenticación en dos fases
- Admite identidades basadas en notificaciones
- Mejor interoperabilidad con ASP.Net Core
Descargar código o Descargar PDF
En este tutorial veremos cómo limitar el acceso a páginas y restringir la funcionalidad a nivel de página mediante diversas técnicas.
Introducción
La mayoría de las aplicaciones web que ofrecen cuentas de usuario lo hacen en parte para restringir que determinados visitantes accedan a determinadas páginas del sitio. En la mayoría de los sitios del panel de mensajes en línea, por ejemplo, todos los usuarios (anónimos y autenticados) pueden ver las publicaciones del panel de mensajes, pero solo los usuarios autenticados pueden visitar la página web para crear una nueva publicación. Y puede haber páginas administrativas que solo sean accesibles para un usuario determinado (o un conjunto determinado de usuarios). Además, la funcionalidad de nivel de página puede diferir por usuario. Al ver una lista de publicaciones, los usuarios autenticados se muestran una interfaz para clasificar cada publicación, mientras que esta interfaz no está disponible para los visitantes anónimos.
ASP.NET facilita la definición de reglas de autorización basadas en el usuario. Con un poco de marcado en Web.config
, se pueden bloquear páginas web específicas o directorios completos para que solo sean accesibles para un subconjunto especificado de usuarios. La funcionalidad de nivel de página se puede activar o desactivar en función del usuario que ha iniciado sesión actualmente a través de medios declarativos y mediante programación.
En este tutorial veremos cómo limitar el acceso a páginas y restringir la funcionalidad a nivel de página mediante diversas técnicas. Comencemos.
Un vistazo al flujo de trabajo de autorización de direcciones URL
Como se describe en el tutorial Introducción a la autenticación de formularios, cuando el tiempo de ejecución de ASP.NET procesa una solicitud de un recurso de ASP.NET, la solicitud genera una serie de eventos durante su ciclo de vida. Los Módulos HTTP son clases administradas cuyo código se ejecuta en respuesta a un evento determinado en el ciclo de vida de la solicitud. ASP.NET incluye una serie de módulos HTTP que realizan tareas esenciales en segundo plano.
Uno de estos módulos HTTP es FormsAuthenticationModule
. Como se explicó en los tutoriales anteriores, la función principal de FormsAuthenticationModule
es determinar la identidad de la solicitud actual. Esto se logra inspeccionando el vale de autenticación de formularios, que se encuentra en una cookie o incrustada dentro de la dirección URL. Esta identificación tiene lugar durante el AuthenticateRequest
evento.
Otro módulo HTTP importante es UrlAuthorizationModule
, que se genera en respuesta al AuthorizeRequest
evento (que sucede después del evento AuthenticateRequest
). UrlAuthorizationModule
Examina el marcado de configuración en Web.config
para determinar si la identidad actual tiene autoridad para visitar la página especificada. Este proceso se conoce como Autorización de direcciones URL.
Examinaremos la sintaxis de las reglas de autorización de direcciones URL en el paso 1, pero primero veremos lo que UrlAuthorizationModule
hace en función de si la solicitud está autorizada o no. Si UrlAuthorizationModule
determina que la solicitud está autorizada, no hace nada y la solicitud continúa a lo largo de su ciclo de vida. Sin embargo, si la solicitud no está autorizada, el anula el ciclo de vida UrlAuthorizationModule
e indica al objeto Response
que devuelva un estado HTTP 401 no autorizado. Cuando se usa la autenticación de formularios, este estado HTTP 401 nunca se devuelve al cliente porque si FormsAuthenticationModule
detecta un estado HTTP 401 lo modifica a un redireccionamiento HTTP 302 al página de registro.
En la figura 1 se muestra el flujo de trabajo de la canalización de ASP.NET FormsAuthenticationModule
, y el UrlAuthorizationModule
cuando llega una solicitud no autorizada. En concreto, la figura 1 muestra una solicitud de un visitante anónimo para ProtectedPage.aspx
, que es una página que deniega el acceso a usuarios anónimos. Dado que el visitante es anónimo, anula la solicitud UrlAuthorizationModule
y devuelve un estado HTTP 401 No autorizado. A continuación, FormsAuthenticationModule
convierte el estado 401 en una redirección 302 a la página de registro. Una vez autenticado el usuario a través de la página de registro, se le redirigirá a ProtectedPage.aspx
. Esta vez, FormsAuthenticationModule
identifica al usuario en función de su vale de autenticación. Ahora que el visitante está autenticado, UrlAuthorizationModule
permite el acceso a la página.
Figura 1: Flujo de trabajo de autenticación y autorización de direcciones URL de formularios (Haga clic para ver la imagen en tamaño completo)
En la figura 1 se muestra la interacción que se produce cuando un visitante anónimo intenta acceder a un recurso que no está disponible para los usuarios anónimos. En tal caso, el visitante anónimo se redirige a la página de inicio de sesión con la página que ha intentado visitar especificada en la cadena de consulta. Una vez que el usuario ha iniciado sesión correctamente, se le redirigirá automáticamente al recurso que inicialmente intentó ver.
Cuando un usuario anónimo realiza la solicitud no autorizada, este flujo de trabajo es sencillo y es fácil para que el visitante comprenda lo que ha ocurrido y por qué. Pero tenga en cuenta que FormsAuthenticationModule
redirigirá a cualquier usuario no autorizado a la página de registro, incluso si la solicitud la realiza un usuario autenticado. Esto puede dar lugar a una experiencia de usuario confusa si un usuario autenticado intenta visitar una página para la que carece de autoridad.
Imagine que nuestro sitio web tenía sus reglas de autorización de dirección URL configuradas de modo que la página de ASP.NET OnlyTito.aspx
solo tenía acceso a Tito. Ahora, imagine que Sam visita el sitio, inicia sesión y a continuación, intente visitar OnlyTito.aspx
. El UrlAuthorizationModule
detendrá el ciclo de vida de la solicitud y devolverá un estado HTTP 401 No autorizado, que el FormsAuthenticationModule
detectará y redirigirá a Sam a la página de registro. Sin embargo, dado que Sam ya se ha registrado, es posible que se pregunte por qué se le ha enviado de vuelta a la página de registro. Podría razonar que sus credenciales de registro se perdieron de alguna manera, o que ha especificado credenciales no válidas. Si Sam vuelve a escribir sus credenciales desde la página de registro, se iniciará sesión (de nuevo) y se le redirigirá a OnlyTito.aspx
. El UrlAuthorizationModule
detectará que Sam no puede visitar esta página y se le devolverá a la página de registro.
En la figura 2 se muestra este flujo de trabajo confuso.
figura 2: El flujo de trabajo predeterminado puede provocar un ciclo confuso (Haga clic para ver la imagen en tamaño completo)
El flujo de trabajo ilustrado en la Figura 2 puede confundir rápidamente incluso al visitante más experto en informática. Veremos formas de evitar este ciclo confuso en el paso 2.
Nota:
ASP.NET usa dos mecanismos para determinar si el usuario actual puede acceder a una página web determinada: autorización de direcciones URL y autorización de archivos. La autorización de archivos se implementa medianteFileAuthorizationModule
, que determina la autoridad mediante una consulta a las ACL solicitadas. La autorización de archivos se usa normalmente con la autenticación de Windows porque las ACL son permisos que se aplican a las cuentas de Windows. Al usar la autenticación de formularios, todas las solicitudes de sistema operativo y de nivel de sistema de archivos se ejecutan mediante la misma cuenta de Windows, independientemente del usuario que visite el sitio. Dado que esta serie de tutoriales se centra en la autenticación de formularios, no se analizará la autorización de archivos.
Ámbito de autorización de dirección URL
El UrlAuthorizationModule
es código administrado que forma parte del entorno de ejecución de ASP.NET. Antes de la versión 7 del servidor web de Internet Information Services (IIS) de Microsoft, había una barrera distinta entre la canalización HTTP de IIS y la canalización del entorno de ejecución de ASP.NET. En resumen, en IIS 6 y anteriores, el ASP.NET UrlAuthorizationModule
solo se ejecuta cuando una solicitud se delega desde IIS al ejecutar de ASP.NET. De manera predeterminada, IIS procesa contenido estático, como páginas HTML y CSS, JavaScript y archivos de imagen, y solo entrega las solicitudes al entorno de ejecución de ASP.NET cuando se solicita una página con una extensión de .aspx
, .asmx
, o .ashx
.
Sin embargo, IIS 7 permite canalizaciones integradas de IIS y ASP.NET. Con algunas opciones de configuración, puede configurar IIS 7 para invocar UrlAuthorizationModule
para todas las solicitudes de , lo que significa que las reglas de autorización de direcciones URL se pueden definir para archivos de cualquier tipo. Además, IIS 7 incluye su propio motor de autorización de direcciones URL. Para obtener más información sobre la integración de ASP.NET y la funcionalidad de autorización de direcciones URL nativas de IIS 7, consulte Descripción de la autorización de direcciones URL de IIS7. Para obtener una visión más detallada de ASP.NET y la integración de IIS 7, recoge una copia del libro de Shahram Khosravi, Professional IIS 7 y ASP.NET Integrated Programming (ISBN: 978-0470152539).
En pocas palabras, en versiones anteriores a IIS 7, las reglas de autorización de direcciones URL solo se aplican a los recursos administrados por el entorno de ejecución de ASP.NET. Pero con IIS 7 es posible usar la característica de autorización de direcciones URL nativas de IIS o integrar ASP. NET UrlAuthorizationModule
en la canalización HTTP de IIS, lo que amplía esta funcionalidad a todas las solicitudes.
Nota:
Existen algunas diferencias sutiles pero importantes en la forma en que ASP.NETUrlAuthorizationModule
y la característica de autorización de direcciones URL de IIS 7 procesan las reglas de autorización. En este tutorial no se examina la funcionalidad de autorización de direcciones URL de IIS 7 ni las diferencias en la forma en que analiza las reglas de autorización en comparación con UrlAuthorizationModule
. Para obtener más información sobre estos temas, consulte la documentación de IIS 7 en MSDN o en www.iis.net.
Paso 1: Definir reglas de autorización de direcciones URL en Web.config
UrlAuthorizationModule
determina si se debe conceder o denegar el acceso a un recurso solicitado para una identidad determinada en función de las reglas de autorización de dirección URL definidas en la configuración de la aplicación. Las reglas de autorización se detallan en el <authorization>
elemento en forma de elemento secundario <allow>
y <deny>
. Cada elemento secundario <allow>
y <deny>
puede especificar:
- Un usuario determinado
- Una lista delimitada por comas de usuarios
- Todos los usuarios anónimos, indicados por un signo de interrogación (?)
- Todos los usuarios, indicados por un asterisco (*)
El marcado siguiente muestra cómo usar las reglas de autorización de dirección URL para permitir a los usuarios Tito y Scott y denegar a todos los demás:
<authorization>
<allow users="Tito, Scott" />
<deny users="*" />
</authorization>
El elemento <allow>
define qué usuarios se permiten (Tito y Scott) mientras que el elemento <deny>
indica que se deniegan todos los usuarios .
Nota:
Los elementos <allow>
y <deny>
también pueden especificar reglas de autorización para los roles. Examinaremos la autorización basada en roles en un tutorial futuro.
La siguiente configuración concede acceso a cualquier persona que no sea Sam (incluidos los visitantes anónimos):
<authorization>
<deny users="Sam" />
</authorization>
Para permitir solo usuarios autenticados, use la siguiente configuración, que deniega el acceso a todos los usuarios anónimos:
<authorization>
<deny users="?" />
</authorization>
Las reglas de autorización se definen dentro del elemento <system.web>
en Web.config
y se aplican a todos los recursos de ASP.NET de la aplicación web. A menudo, una aplicación tiene reglas de autorización diferentes para diferentes secciones. Por ejemplo, en un sitio de comercio electrónico, todos los visitantes pueden examinar los productos, ver reseñas de productos, buscar en el catálogo, etc. Sin embargo, solo los usuarios autenticados pueden llegar a la desprotección o las páginas para administrar el historial de envíos de uno. Además, puede haber partes del sitio a las que solo pueden acceder los usuarios seleccionados, como los administradores del sitio.
ASP.NET facilita la definición de diferentes reglas de autorización para diferentes archivos y carpetas del sitio. Las reglas de autorización especificadas en el archivo Web.config
de la carpeta raíz se aplican a todos los recursos ASP.NET del sitio. Sin embargo, esta configuración de autorización predeterminada puede anularse para una carpeta concreta agregando un Web.config
con una sección <authorization>
.
Vamos a actualizar nuestro sitio web para que solo los usuarios autenticados puedan visitar las páginas de ASP.NET de la carpeta Membership
. Para ello, es necesario agregar un archivo Web.config
a la carpeta Membership
y establecer su configuración de autorización para denegar a los usuarios anónimos. Haga clic con el botón derecho en la carpeta Membership
en el Explorador de soluciones, elija el menú Agregar nuevo elemento en el menú contextual y agregue un nuevo archivo de configuración web denominado Web.config
.
Figura 3: Agregar un archivo Web.config
a la carpeta Membership
(Haga clic para ver la imagen en tamaño completo)
En este punto, el proyecto debe contener dos archivosWeb.config
: uno en el directorio raíz y otro en la carpetaMembership
.
Figura 4: Su aplicación debe contener ahora dos archivos Web.config
(Haga clic para ver la imagen en tamaño completo)
Actualice el archivo de configuración de la carpeta Membership
para prohibir el acceso a usuarios anónimos.
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</configuration>
Eso es todo.
Para probar este cambio, visite la página principal en un explorador y asegúrese de que ha cerrado la sesión. Dado que el comportamiento predeterminado de una aplicación de ASP.NET es permitir a todos los visitantes, y dado que no hemos realizado ninguna modificación de autorización en el archivo Web.config
del directorio raíz, podemos visitar los archivos en el directorio raíz como visitante anónimo.
Haga clic en el vínculo Crear cuentas de usuario que se encuentra en la columna izquierda. Esto le llevará al ~/Membership/CreatingUserAccounts.aspx
. Dado que el archivo Web.config
de la carpeta Membership
define reglas de autorización para prohibir el acceso anónimo, anula la solicitud UrlAuthorizationModule
y devuelve un estado HTTP 401 No autorizado. El FormsAuthenticationModule
lo modifica a un estado de redirección 302, enviándonos a la página de inicio de sesión. Tenga en cuenta que la página a la que intentamos acceder (CreatingUserAccounts.aspx
) se pasa a la página de inicio de sesión a través del parámetro querystring ReturnUrl
.
Figura 5: Dado que las reglas de autorización de direcciones URL prohíben el acceso anónimo, se redirige a la página de inicio de sesión (Haga clic para ver la imagen en tamaño completo)
Después de iniciar sesión correctamente, se redirige a la página CreatingUserAccounts.aspx
. Esta vez, UrlAuthorizationModule
permite el acceso a la página porque ya no somos anónimos.
Aplicación de reglas de autorización de direcciones URL a una ubicación específica
La configuración de autorización definida en la sección <system.web>
de Web.config
se aplica a todos los recursos de ASP.NET de ese directorio y sus subdirectorios (hasta que otro archivo Web.config
invalide lo contrario). Sin embargo, en algunos casos, es posible que deseemos que todos los recursos de ASP.NET de un directorio determinado tengan una configuración de autorización determinada, excepto para una o dos páginas específicas. Esto se puede lograr agregando un elemento <location>
en Web.config
, apuntando al archivo cuyas reglas de autorización difieren y definiendo sus reglas de autorización únicas en ella.
Para ilustrar el uso del elemento <location>
para invalidar los valores de configuración de un recurso específico, vamos a personalizar la configuración de autorización para que solo Tito pueda visitar CreatingUserAccounts.aspx
. Para ello, agregue un elemento <location>
al archivo Web.config
de la carpeta Membership
y actualice su marcado para que tenga el siguiente aspecto:
<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
<location path="CreatingUserAccounts.aspx">
<system.web>
<authorization>
<allow users="Tito" />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
El elemento <authorization>
en <system.web>
define las reglas de autorización de direcciones URL predeterminadas para recursos ASP.NET de la carpeta Membership
y sus subcarpetas. El elemento <location>
nos permite invalidar estas reglas para un recurso determinado. En el marcado anterior, el elemento <location>
hace referencia a la página CreatingUserAccounts.aspx
y especifica sus reglas de autorización como permitir a Tito, pero denegar a todos los demás.
Para probar este cambio de autorización, comience visitando el sitio web como un usuario anónimo. Si intenta visitar cualquier página de la carpeta Membership
, como UserBasedAuthorization.aspx
, el UrlAuthorizationModule
denegará la solicitud y se le redirigirá a la página de inicio de sesión. Después de iniciar sesión como, por ejemplo, Scott, puede visitar cualquier página de la carpeta Membership
excepto para CreatingUserAccounts.aspx
. Si intenta visitar CreatingUserAccounts.aspx
ha iniciado sesión como cualquiera, pero Tito dará lugar a un intento de acceso no autorizado, lo que le redirigirá de nuevo a la página de inicio de sesión.
Nota:
El elemento <location>
debe aparecer fuera del elemento<system.web>
de la configuración. Debe usar un elemento <location>
independiente para cada recurso cuya configuración de autorización quiera invalidar.
Vea cómo UrlAuthorizationModule
usa las reglas de autorización para conceder o denegar el acceso
UrlAuthorizationModule
determina si se debe autorizar una identidad determinada para una dirección URL determinada mediante el análisis de las reglas de autorización de direcciones URL una a la vez, empezando por la primera y trabajando hacia abajo. Tan pronto como se encuentre una coincidencia, se concede o deniega el acceso al usuario, en función de si la coincidencia se encontró en un elemento <allow>
o <deny>
. Si no se encuentra ninguna coincidencia, se concede acceso al usuario. Por lo tanto, si desea restringir el acceso a una o varias cuentas de usuario, es imperativo usar un elemento <deny>
como último elemento en la configuración de autorización de dirección URL. Si omite un elemento<deny>
, se concederá acceso a todos los usuarios.
Para comprender mejor el proceso utilizado por la UrlAuthorizationModule
autoridad para determinar la autoridad, considere las reglas de autorización de direcciones URL de ejemplo que hemos examinado anteriormente en este paso. La primera regla es un elemento <allow>
que permite el acceso a Tito y Scott. La segunda regla es un elemento <deny>
que deniega el acceso a todos. Si nos visita un usuario anónimo, UrlAuthorizationModule
empieza preguntando: ¿Es anónimo Scott o Tito? La respuesta, obviamente, es No, por lo que continúa con la segunda regla. ¿Está anónimo en el conjunto de todos? Dado que la respuesta aquí es Sí, la regla <deny>
se aplica y el visitante se redirige a la página de inicio de sesión. Del mismo modo, si Jisun está de visita, UrlAuthorizationModule
empieza preguntando: ¿Jisun es Scott o Tito? Como no es, el UrlAuthorizationModule
continúa con la segunda pregunta, ¿Es Jisun en el conjunto de todos? Lo está, así que a ella también se le deniega el acceso. Por último, en caso de que Tito realice una visita, la primera pregunta planteada por UrlAuthorizationModule
es una respuesta afirmativa, por lo que se concede acceso a Tito.
Dado que UrlAuthorizationModule
procesa las reglas de autorización de arriba hacia abajo, deteniéndose en cualquier coincidencia, es importante que las reglas más específicas vayan antes que las menos específicas. Es decir, para definir reglas de autorización que prohíben a los usuarios Jisun y anónimos, pero permitir a todos los demás usuarios autenticados, empezaría con la regla más específica (la que afecta a Jisun) y a continuación, continuar con las reglas menos específicas, las que permiten a todos los demás usuarios autenticados, pero denegar a todos los usuarios anónimos. Las siguientes reglas de autorización de direcciones URL implementan esta directiva denegando primero Jisun y, a continuación, denegando a cualquier usuario anónimo. A cualquier usuario autenticado que no sea Jisun se le concederá acceso porque ninguna de estas <deny>
instrucciones coincidirá.
<authorization>
<deny users="Jisun" />
<deny users="?" />
</authorization>
Paso 2: Corregir el flujo de trabajo para usuarios no autorizados y autenticados
Como se ha descrito anteriormente en este tutorial en la sección A Look at the URL Authorization Workflow (Un vistazo al flujo de trabajo de autorización de dirección URL), siempre que transcurra una solicitud no autorizada, UrlAuthorizationModule
anula la solicitud y devuelve un estado HTTP 401 No autorizado. Este estado 401 lo modifica FormsAuthenticationModule
en un estado de redirección 302 que envía al usuario a la página de inicio de sesión. Este flujo de trabajo se produce en cualquier solicitud no autorizada, incluso si el usuario está autenticado.
Devolver un usuario autenticado a la página de inicio de sesión es probable que los confunda, ya que ya han iniciado sesión en el sistema. Con un poco de trabajo, podemos mejorar este flujo de trabajo mediante la redirección de usuarios autenticados que realizan solicitudes no autorizadas a una página que explica que han intentado acceder a una página restringida.
Empiece por crear una nueva página de ASP.NET en la carpeta raíz de la aplicación web denominada UnauthorizedAccess.aspx
; no olvide asociar esta página a la Site.master
página maestra. Después de crear esta página, quite el control Content que hace referencia a LoginContent
ContentPlaceHolder para que se muestre el contenido predeterminado de la página maestra. A continuación, agregue un mensaje que explique la situación, es decir, que el usuario intentó acceder a un recurso protegido. Después de agregar este mensaje, el marcado declarativo de la página UnauthorizedAccess.aspx
debe tener un aspecto similar al siguiente:
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeFile="UnauthorizedAccess.aspx.cs" Inherits="UnauthorizedAccess"
Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server">
<h2>Unauthorized Access</h2>
<p>
You have attempted to access a page that you are not authorized to view.
</p>
<p>
If you have any questions, please contact the site administrator.
</p>
</asp:Content>
Ahora es necesario modificar el flujo de trabajo para que, si un usuario autenticado realiza una solicitud no autorizada, se envía a la página UnauthorizedAccess.aspx
en lugar de a la página de registro. La lógica que redirige las solicitudes no autorizadas a la página de inicio de sesión se encuentra dentro de un método privado de la clase FormsAuthenticationModule
, por lo que no podemos personalizar este comportamiento. Sin embargo, lo que podemos hacer es agregar nuestra propia lógica a la página de registro que redirige al usuario UnauthorizedAccess.aspx
, si es necesario.
Cuando FormsAuthenticationModule
redirige a un visitante no autorizado a la página de registro, anexa la dirección URL solicitada y no autorizada a la cadena de consulta con el nombre ReturnUrl
. Por ejemplo, si un usuario no autorizado intentó visitar OnlyTito.aspx
, el FormsAuthenticationModule
los redirigiría a Login.aspx?ReturnUrl=OnlyTito.aspx
. Por lo tanto, si un usuario autenticado llega a la página de registro con una cadena de consultas que incluye el parámetro ReturnUrl
, sabemos que este usuario no autenticado acaba de intentar visitar una página que no está autorizada para ver. En tal caso, queremos redirigirla a UnauthorizedAccess.aspx
.
Para ello, agregue el código siguiente al controlador de eventos Page_Load
de la página de registro:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
// This is an unauthorized, authenticated request...
Response.Redirect("~/UnauthorizedAccess.aspx");
}
}
El código anterior redirige a los usuarios autenticados y no autorizados a la página UnauthorizedAccess.aspx
. Para ver esta lógica en acción, visite el sitio como visitante anónimo y haga clic en el vínculo Crear cuentas de usuario en la columna izquierda. Esto le llevará a la página ~/Membership/CreatingUserAccounts.aspx
, que en el paso 1 hemos configurado para permitir solo el acceso a Tito. Dado que los usuarios anónimos están prohibidos, el FormsAuthenticationModule
nos redirige de nuevo a la página de registro.
En este momento somos anónimos, por lo que Request.IsAuthenticated
devuelve false
y no se redirige a UnauthorizedAccess.aspx
. En su lugar, se muestra la página de inicio de sesión. Inicie sesión como usuario distinto de Tito, como Bruce. Después de escribir las credenciales adecuadas, la página de inicio de sesión nos redirige a ~/Membership/CreatingUserAccounts.aspx
. Sin embargo, dado que esta página solo es accesible para Tito, no estamos autorizados para verlo y se devuelven rápidamente a la página de inicio de sesión. Sin embargo, esta vez Request.IsAuthenticated
devuelve true
(y el parámetro querystring de ReturnUrl
existe), por lo que se redirige a la página UnauthorizedAccess.aspx
.
Figura 6: Autenticado, los usuarios no autorizados se redirigen a UnauthorizedAccess.aspx
(haga clic para ver la imagen en tamaño completo)
Este flujo de trabajo personalizado presenta una experiencia de usuario más sensible y sencilla al cortocircuitar el ciclo que se muestra en la figura 2.
Paso 3: Limitar la funcionalidad basada en el usuario que ha iniciado sesión actualmente
La autorización de direcciones URL facilita la especificación de reglas de autorización generales. Como vimos en el paso 1, con la autorización de dirección URL podemos indicar concisamente qué identidades se permiten y cuáles se deniegan de ver una página determinada o todas las páginas de una carpeta. Sin embargo, en determinados escenarios, es posible que deseemos permitir que todos los usuarios visiten una página, pero limite la funcionalidad de la página en función del usuario que lo visite.
Considere el caso de un sitio web de comercio electrónico que permite a los visitantes autenticados revisar sus productos. Cuando un usuario anónimo visita la página de un producto, solo vería la información del producto y no se le daría la oportunidad de dejar una revisión. Sin embargo, un usuario autenticado que visita la misma página vería la interfaz de revisión. Si el usuario autenticado aún no había revisado este producto, la interfaz les permitiría enviar una revisión; de lo contrario, mostraría su revisión enviada anteriormente. Para seguir este escenario, la página del producto podría mostrar información adicional y ofrecer características extendidas para aquellos usuarios que trabajan para la empresa de comercio electrónico. Por ejemplo, la página del producto podría enumerar el inventario en existencias e incluir opciones para editar el precio y la descripción del producto cuando un empleado visita.
Estas reglas de autorización detalladas se pueden implementar mediante declaración o mediante programación (o mediante alguna combinación de los dos). En la sección siguiente veremos cómo implementar una autorización específica mediante el control LoginView. Después, exploraremos técnicas de programación. Sin embargo, para poder examinar la aplicación de reglas de autorización específicas, primero es necesario crear una página cuya funcionalidad depende del usuario que lo visite.
Vamos a crear una página que muestre los archivos de un directorio determinado dentro de GridView. Junto con enumerar el nombre, el tamaño y otra información de cada archivo, GridView incluirá dos columnas de LinkButtons: una titulada View y una titulada Delete. Si se hace clic en View LinkButton (Ver LinkButton), se mostrará el contenido del archivo seleccionado; Si se hace clic en Delete LinkButton, se eliminará el archivo. Vamos a crear inicialmente esta página de modo que su funcionalidad de vista y eliminación esté disponible para todos los usuarios. En las secciones Using the LoginView Control and Programmatically Limiting Functionality (Usar el control LoginView y limitar la funcionalidad mediante programación), veremos cómo habilitar o deshabilitar estas características en función del usuario que visita la página.
Nota:
La página de ASP.NET que estamos a punto de compilar usa un control GridView para mostrar una lista de archivos. Dado que esta serie de tutoriales se centra en la autenticación, autorización, cuentas de usuario y roles de formularios, no quiero dedicar demasiado tiempo a analizar los trabajos internos del control GridView. Aunque en este tutorial se proporcionan instrucciones paso a paso específicas para configurar esta página, no profundiza en los detalles de por qué se realizaron determinadas opciones o qué efecto tienen las propiedades concretas en la salida representada. Para obtener un examen completo del control GridView, consulte mi tutorialTrabajar con datos en la serie ASP.NET 2.0.
Para empezar, abra el archivo UserBasedAuthorization.aspx
en la carpeta Membership
y agregue un control GridView a la página denominada FilesGrid
. En la etiqueta inteligente de GridView, haga clic en el vínculo Editar columnas para iniciar el cuadro de diálogo Campos. Desde aquí, desactive la casilla Generar campos automáticamente en la esquina inferior izquierda. A continuación, agregue un botón Seleccionar, un botón Eliminar y dos BoundFields desde la esquina superior izquierda (los botones Seleccionar y Eliminar se pueden encontrar en el tipo CommandField). Establezca la propiedad SelectText
del botón Seleccionar en Ver y las propiedades HeaderText
y DataField
del primer BoundField en Nombre. Establezca la segunda propiedad HeaderText
de BoundField en Size en Bytes, su propiedad DataField
en Length, su propiedad DataFormatString
en {0:N0} y su propiedad HtmlEncode
para False.
Después de configurar las columnas de GridView, haga clic en Aceptar para cerrar el cuadro de diálogo Campos. En la ventana Propiedades, establezca la propiedad DataKeyNames
GridView en FullName
. En este momento, el marcado declarativo de GridView debe tener un aspecto similar al siguiente:
<asp:GridView ID="FilesGrid" DataKeyNames="FullName" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:CommandField SelectText="View" ShowSelectButton="True"/>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:BoundField DataField="Length" DataFormatString="{0:N0}"
HeaderText="Size in Bytes" HtmlEncode="False" />
</Columns>
</asp:GridView>
Con el marcado de GridView creado, estamos listos para escribir el código que recuperará los archivos de un directorio determinado y los enlazará a GridView. Agregue el código siguiente al controlador de eventos de la página Page_Load
:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string appPath = Request.PhysicalApplicationPath;
DirectoryInfo dirInfo = new DirectoryInfo(appPath);
FileInfo[] files = dirInfo.GetFiles();
FilesGrid.DataSource = files;
FilesGrid.DataBind();
}
}
El código anterior usa la DirectoryInfo
clase para obtener una lista de los archivos de la carpeta raíz de la aplicación. El GetFiles()
método devuelve todos los archivos del directorio como una matriz de FileInfo
objetos, que se enlaza a GridView. El objeto FileInfo
tiene una variedad de propiedades, como Name
, Length
, y IsReadOnly
, entre otros. Como puede ver en su marcado declarativo, GridView muestra solo las propiedades Name
y Length
.
Nota:
Las clases DirectoryInfo
y FileInfo
se encuentran en el System.IO
espacio de nombres. Por lo tanto, deberá anteponer estos nombres de clase con sus nombres de espacio de nombres o tener el espacio de nombres importado en el archivo de clase (a través de using System.IO
).
Dedique un momento a visitar esta página a través de un explorador. Mostrará la lista de archivos que residen en el directorio raíz de la aplicación. Al hacer clic en cualquiera de los elementos View o Delete LinkButtons, se producirá un postback, pero no se producirá ninguna acción porque todavía tenemos que crear los controladores de eventos necesarios.
Figura 7: GridView enumera los archivos en el directorio raíz de la aplicación web (haga clic para ver la imagen en tamaño completo)
Necesitamos un medio para mostrar el contenido del archivo seleccionado. Vuelva a Visual Studio y agregue un TextBox denominado FileContents
encima de GridView. Establezca la propiedad TextMode
en MultiLine
y las propiedades Columns
y Rows
en 95 % y 10, respectivamente.
<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>
A continuación, cree un controlador de eventos para el SelectedIndexChanged
evento de GridView y agregue el código siguiente:
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
// Open the file and display it
string fullFileName = FilesGrid.SelectedValue.ToString();
string contents = File.ReadAllText(fullFileName);
FileContents.Text = contents;
}
Este código usa la propiedad SelectedValue
de GridView para determinar el nombre de archivo completo del archivo seleccionado. Internamente, se hace referencia a la colección DataKeys
para obtener el SelectedValue
, por lo que es imperativo establecer la propiedad DataKeyNames
de GridView en Name, como se ha descrito anteriormente en este paso. La File
clase se usa para leer el contenido del archivo seleccionado en una cadena, que a continuación se asigna a la propiedadFileContents
TextBoxText
, mostrando así el contenido del archivo seleccionado en la página.
Figura 8: El contenido del archivo seleccionado se muestra en TextBox (Haga clic para ver la imagen en tamaño completo)
Nota:
Si ve el contenido de un archivo que contiene el marcado HTML y a continuación, intenta ver o eliminar un archivo, recibirá un HttpRequestValidationException
. Esto ocurre porque en postback el contenido de TextBox se devuelve al servidor web. De manera predeterminada, ASP.NET genera un error HttpRequestValidationException
cada vez que se detecta contenido de postback potencialmente peligroso, como el marcado HTML. Para deshabilitar este error al producirse, desactive la validación de solicitudes para la página agregando ValidateRequest="false"
a la directiva @Page
. Para obtener más información sobre las ventajas de la validación de solicitudes, así como las precauciones que debe tomar al deshabilitarla, lea Validación de solicitudes: prevención de ataques de script .
Por último, agregue un controlador de eventos con el código siguiente para el RowDeleting
evento GridView:
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
string fullFileName = FilesGrid.DataKeys[e.RowIndex].Value.ToString();
FileContents.Text = string.Format("You have opted to delete {0}.", fullFileName);
// To actually delete the file, uncomment the following line
// File.Delete(fullFileName);
}
El código simplemente muestra el nombre completo del archivo que se va a eliminar en FileContents
TextBoxsineliminar realmente el archivo.
Figura 9: Al hacer clic en el botón Eliminar no se elimina realmente el archivo (Haga clic para ver la imagen en tamaño completo)
En el paso 1 hemos configurado las reglas de autorización de direcciones URL para impedir que los usuarios anónimos vean las páginas de la carpeta Membership
. Para mostrar mejor la autenticación detallada, vamos a permitir que los usuarios anónimos visiten la páginaUserBasedAuthorization.aspx
, pero con funcionalidad limitada. Para que todos los usuarios tengan acceso a esta página, añada el siguiente elemento <location>
al archivo Web.config
de la carpetaMembership
:
<location path="UserBasedAuthorization.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Después de agregar este elemento <location>
, pruebe las nuevas reglas de autorización de dirección URL cerrando la sesión en el sitio. Como usuario anónimo se le debería permitir visitar la página UserBasedAuthorization.aspx
.
Actualmente, cualquier usuario autenticado o anónimo puede visitar la página UserBasedAuthorization.aspx
y ver o eliminar archivos. Hagámoslo de forma que solo los usuarios autentificados puedan ver el contenido de un archivo y solo Tito pueda eliminar un archivo. Estas reglas de autorización detalladas se pueden aplicar mediante declaración, mediante programación o mediante una combinación de ambos métodos. Vamos a usar el enfoque declarativo para limitar quién puede ver el contenido de un archivo; Usaremos el enfoque mediante programación para limitar quién puede eliminar un archivo.
Uso del control LoginView
Como hemos visto en los tutoriales anteriores, el control LoginView es útil para mostrar diferentes interfaces para usuarios autenticados y anónimos, y ofrece una manera fácil de ocultar la funcionalidad que no es accesible para los usuarios anónimos. Dado que los usuarios anónimos no pueden ver o eliminar archivos, solo es necesario mostrar el TextBox FileContents
cuando un usuario autenticado visita la página. Para conseguirlo, añada un control LoginView a la página, nómbrelo LoginViewForFileContentsTextBox
, y mueva el marcado declarativo FileContents
de TextBox al LoggedInTemplate
del control LoginView.
<asp:LoginView ID=" LoginViewForFileContentsTextBox " runat="server">
<LoggedInTemplate>
<p>
<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>
</p>
</LoggedInTemplate>
</asp:LoginView>
Los controles Web de las plantillas de LoginView ya no son accesibles directamente desde la clase de código subyacente. Por ejemplo, los controladores de eventos FilesGrid
y SelectedIndexChanged
de RowDeleting
GridView hacen referencia actualmente al control FileContents
de TextBox con código como:
FileContents.Text = text;
Sin embargo, este código ya no es válido. Al mover el TextBox FileContents
al LoggedInTemplate
no se puede acceder directamente al control TextBox. En su lugar, debemos utilizar el método FindControl("controlId")
para hacer referencia al control mediante programación. Actualice los controladores de eventos de FilesGrid
para hacer referencia a TextBox de la siguiente manera:
TextBox FileContentsTextBox = LoginViewForFileContentsTextBox.FindControl("FileContents") as TextBox;
FileContentsTextBox.Text = text;
Después de mover el TextBox al LoggedInTemplate
del LoginView y actualizar el código de la página para hacer referencia al TextBox utilizando el patrón FindControl("controlId")
, visite la página como usuario anónimo. Como muestra la Figura 10, el TextBox FileContents
no se muestra. Sin embargo, todavía se muestra LinkButton de Vista.
Figura 10: El control LoginView solo representa el TextBox FileContents
para usuarios autenticados (haga clic para ver la imagen en tamaño completo)
Una forma de ocultar el botón Vista para usuarios anónimos es convertir el campo GridView en un TemplateField. Esto generará una plantilla que contiene el marcado declarativo para view LinkButton. Podemos entonces añadir un control LoginView al TemplateField y colocar el LinkButton en el LoggedInTemplate
del LoginView, ocultando así el botón Vista a los visitantes anónimos. Para ello, haga clic en el vínculo Editar columnas de la etiqueta inteligente de GridView para iniciar el cuadro de diálogo Campos. A continuación, seleccione el botón Seleccionar de la lista situada en la esquina inferior izquierda y haga clic en el vínculo Convertir este campo en un TemplateField. Al hacerlo, se modificará el marcado declarativo del campo desde:
<asp:CommandField SelectText="View" ShowSelectButton="True"/>
A:
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
CommandName="Select" Text="View"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
En este punto, podemos agregar un elemento LoginView a TemplateField. El marcado siguiente muestra view LinkButton only for authenticated users (Ver linkButton solo para usuarios autenticados).
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
CommandName="Select" Text="View"></asp:LinkButton>
</LoggedInTemplate>
</asp:LoginView>
</ItemTemplate>
</asp:TemplateField>
Como se muestra en la figura 11, el resultado final no es tan bonito como la columna Vista todavía se muestra aunque los LinkButtons de Vista de la columna estén ocultos. En la siguiente sección veremos cómo ocultar toda la columna GridView (y no sólo el LinkButton).
Figura 11: el control LoginView oculta los botones ver LinkButtons para visitantes anónimos (Haga clic para ver la imagen en tamaño completo)
Limitación de la funcionalidad mediante programación
En algunas circunstancias, las técnicas declarativas no son suficientes para limitar la funcionalidad a una página. Por ejemplo, la disponibilidad de determinadas funcionalidades de página puede depender de criterios más allá de si el usuario que visita la página es anónimo o autenticado. En tales casos, los distintos elementos de la interfaz de usuario pueden mostrarse u ocultarse a través de medios se programación.
Para limitar la funcionalidad mediante programación, es necesario realizar dos tareas:
- Determinar si el usuario que visita la página puede acceder a la funcionalidad y
- Modifique mediante programación la interfaz de usuario en función de si el usuario tiene acceso a la funcionalidad en cuestión.
Para demostrar la aplicación de estas dos tareas, solo permitiremos que Tito elimine los archivos de GridView. Entonces, nuestra primera tarea, es determinar si Tito está visitando la página. Una vez que se haya determinado, es necesario ocultar (o mostrar) la columna Eliminar de GridView. Las columnas del GridView son accesibles a través de su propiedad Columns
; una columna solo se muestra si su propiedad Visible
está establecida true
(el valor predeterminado).
Agregue el código siguiente al controlador de eventos Page_Load
antes de enlazar los datos a GridView:
// Is this Tito visiting the page?
string userName = User.Identity.Name;
if (string.Compare(userName, "Tito", true) == 0)
// This is Tito, SHOW the Delete column
FilesGrid.Columns[1].Visible = true;
else
// This is NOT Tito, HIDE the Delete column
FilesGrid.Columns[1].Visible = false;
Como se describe en el tutorial An Overview of Forms Authentication (Introducción a la autenticación de formularios), User.Identity.Name
devuelve el nombre de la identidad. Corresponde al nombre de usuario introducido en el control Inicio de sesión. Si es Tito que visita la página, la propiedad Visible
de la segunda columna de GridView se establece en true
; de lo contrario, se establece en false
. El resultado neto es que cuando alguien distinto de Tito visita la página, ya sea otro usuario autenticado o un usuario anónimo, la columna Eliminar no se representa (vea la figura 12); sin embargo, cuando Tito visita la página, la columna Eliminar está presente (vea la figura 13).
Figura 12: La columna Eliminar no se representa cuando alguien que no sea Tito (por ejemplo, Bruce) (haga clic para ver la imagen en tamaño completo)
Figura 13: La columna Eliminar se representa en relación a Tito (haga clic para ver la imagen en tamaño completo)
Paso 4: Aplicar reglas de autorización basadas en roles a clases y métodos
En el Paso 3 no se le permite a los usuarios anónimos ver el contenido de un archivo y a todos los usuarios excepto Tito eliminar archivos. Esto se consiguió ocultando los elementos de interfaz de usuario asociados para visitantes no autorizados mediante técnicas declarativas y de programación. En nuestro ejemplo sencillo, ocultar correctamente los elementos de la interfaz de usuario era sencillo, pero ¿qué ocurre con sitios más complejos en los que puede haber muchas maneras diferentes de realizar la misma funcionalidad? Al limitar esa funcionalidad a usuarios no autorizados, ¿qué ocurre si olvidamos ocultar o deshabilitar todos los elementos de la interfaz de usuario aplicables?
Una manera fácil de asegurarse de que un usuario no autorizado pueda tener acceso a una determinada funcionalidad es decorar esa clase o método con el PrincipalPermission
atributo. Cuando el entorno de ejecución de .NET usa una clase o ejecuta uno de sus métodos, comprueba que el contexto de seguridad actual tiene permiso para usar la clase o ejecutar el método. El atributo PrincipalPermission
proporciona un mecanismo a través del cual podemos definir estas reglas.
Vamos a demostrar el uso del atributo PrincipalPermission
en el GridView SelectedIndexChanged
y controlador de eventos RowDeleting
para prohibir la ejecución por parte de usuarios anónimos y usuarios distintos de Tito, respectivamente. Todo lo que necesitamos hacer es agregar el atributo adecuado en cada definición de función:
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
...
}
[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
...
}
El atributo del controlador de eventos SelectedIndexChanged
determina que solo los usuarios autenticados pueden ejecutar el controlador de eventos, mientras que el atributo del controlador de eventos RowDeleting
limita la ejecución a Tito.
Si, de alguna manera, un usuario distinto de Tito intenta ejecutar el controlador de eventos RowDeleting
o un usuario no autenticado intenta ejecutar el controlador de eventos SelectedIndexChanged
, el entorno de ejecución de .NET generará un SecurityException
.
Figura 14: Si el contexto de seguridad no está autorizado para ejecutar el método, se produce una SecurityException
(haga clic para ver la imagen en tamaño completo)
Nota:
Para permitir que varios contextos de seguridad accedan a una clase o método, decore la clase o el método con un atributo PrincipalPermission
para cada contexto de seguridad. Es decir, para permitir que Tito y Bruce ejecuten el controlador de eventos RowDeleting
, agregue dos atributos PrincipalPermission
:
[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
[PrincipalPermission(SecurityAction.Demand, Name="Bruce")]
Además de las páginas ASP.NET, muchas aplicaciones también tienen una arquitectura que incluye varias capas, como la lógica de negocios y las capas de acceso a datos. Estas capas normalmente se implementan como bibliotecas de clases y ofrecen clases y métodos para realizar la funcionalidad relacionada con la lógica de negocios y los datos. El atributo PrincipalPermission
también es útil para aplicar reglas de autorización a estas capas.
Para obtener más información sobre el uso del atributo PrincipalPermission
para definir reglas de autorización en clases y métodos, consulte la entrada de blog de Scott GuthrieCómo agregar reglas de autorización a capas de datos y empresariales mediante PrincipalPermissionAttributes
.
Resumen
En este tutorial hemos visto cómo aplicar reglas de autorización basadas en el usuario. Empezamos con un vistazo a ASP. Marco de autorización de direcciones URL de NET. En cada solicitud, el UrlAuthorizationModule
del motor de ASP.NET inspecciona las reglas de autorización de dirección URL definidas en la configuración de la aplicación para determinar si la identidad está autorizada para acceder al recurso solicitado. En resumen, la autorización de direcciones URL facilita la especificación de reglas de autorización para una página específica o para todas las páginas de un directorio determinado.
El marco de autorización de direcciones URL aplica reglas de autorización página por página. Con la autorización de dirección URL, la identidad solicitante está autorizada para acceder a un recurso determinado o no. Sin embargo, muchos escenarios requieren reglas de autorización más específicas. En lugar de definir quién tiene permiso para acceder a una página, es posible que tengamos que permitir que todos accedan a una página, pero mostrar datos diferentes o ofrecer funcionalidades diferentes en función del usuario que visita la página. La autorización a nivel de página generalmente implica ocultar elementos específicos de la interfaz de usuario para evitar que usuarios no autorizados accedan a funciones prohibidas. Además, es posible usar atributos para restringir el acceso a las clases y la ejecución de sus métodos para determinados usuarios.
¡Feliz programación!
Lecturas adicionales
Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Adición de reglas de autorización a las capas de negocio y de datos mediante
PrincipalPermissionAttributes
- Autorización de ASP.NET
- Cambios entre la seguridad de IIS6 e IIS7
- Configuración de Files y subdirectorios específicos
- Limitar la funcionalidad de modificación de datos en función del usuario
- Inicios rápidos del control LoginView
- Descripción de la autorización de direcciones URL de IIS7
UrlAuthorizationModule
Documentación técnica- Trabajar con datos en ASP.NET 2.0
Acerca del autor
Scott Mitchell, autor de varios libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, formador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Se puede contactar con Scott en mitchell@4guysfromrolla.com o a través de su blog en http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores que fueron de gran ayuda. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, escríbame mitchell@4GuysFromRolla.com.