Compartir a través de


Protección de aplicaciones mediante autenticación y autorización

por Microsoft

Descargar PDF

Este es el paso 9 de un tutorial gratuito de aplicación "NerdDinner" que le guía a través de cómo compilar una aplicación web pequeña, pero completa con ASP.NET MVC 1.

El paso 9 muestra cómo agregar autenticación y autorización para proteger nuestra aplicación NerdDinner, de modo que los usuarios necesiten registrarse e iniciar sesión en el sitio para crear nuevas cenas, y solo el usuario que hospeda una cena puede editarlo más adelante.

Si usas ASP.NET MVC 3, te recomendamos seguir los tutoriales introducción a MVC 3 o MVC Music Store .

NerdDinner Paso 9: Autenticación y autorización

En este momento, nuestra aplicación NerdDinner concede a cualquier persona que visite el sitio la capacidad de crear y editar los detalles de cualquier cena. Vamos a cambiar esto para que los usuarios necesiten registrarse e iniciar sesión en el sitio para crear nuevas cenas y agregar una restricción para que solo el usuario que hospeda una cena pueda editarlo más adelante.

Para habilitar esto, usaremos la autenticación y la autorización para proteger nuestra aplicación.

Descripción de la autenticación y autorización

La autenticación es el proceso de identificación y validación de la identidad de un cliente que accede a una aplicación. En pocas palabras, se trata de identificar "quién" es el usuario final cuando visitan un sitio web. ASP.NET admite varias maneras de autenticar a los usuarios del explorador. Para las aplicaciones web de Internet, el enfoque de autenticación más común que se usa se denomina "Autenticación de formularios". La autenticación de formularios permite a un desarrollador crear un formulario de inicio de sesión HTML dentro de su aplicación y, a continuación, validar el nombre de usuario o contraseña que un usuario final envía en una base de datos u otro almacén de credenciales de contraseña. Si la combinación de nombre de usuario y contraseña es correcta, el desarrollador puede pedir a ASP.NET emitir una cookie HTTP cifrada para identificar al usuario en futuras solicitudes. Usaremos la autenticación de formularios con nuestra aplicación NerdDinner.

La autorización es el proceso de determinar si un usuario autenticado tiene permiso para acceder a una dirección URL o recurso determinado o para realizar alguna acción. Por ejemplo, en nuestra aplicación NerdDinner, queremos autorizar que solo los usuarios que inicien sesión puedan acceder a la dirección URL /Dinners/Create y crear cenas nuevas. También queremos agregar lógica de autorización para que solo el usuario que hospeda una cena pueda editarlo y denegar el acceso de edición a todos los demás usuarios.

Autenticación de formularios y AccountController

La plantilla de proyecto predeterminada de Visual Studio para ASP.NET MVC habilita automáticamente la autenticación de formularios cuando se crean aplicaciones ASP.NET MVC nuevas. También agrega automáticamente una implementación de página de inicio de sesión de cuenta pregenerada al proyecto, lo que facilita la integración de la seguridad dentro de un sitio.

La página maestra Site.master predeterminada muestra un vínculo "Iniciar sesión" en la parte superior derecha del sitio cuando el usuario que accede a él no está autenticado:

Captura de pantalla de la página de Nerd Dinner Organizar una Cena. Iniciar sesión está resaltado en la esquina superior derecha.

Al hacer clic en el vínculo "Iniciar sesión" se lleva un usuario a la dirección URL /Account/LogOn :

Captura de pantalla de la página de inicio de sesión de Nerd Dinner.

Los visitantes que no se hayan registrado pueden hacerlo haciendo clic en el vínculo "Registrar", que los llevará a la dirección URL /Account/Register y les permitirá escribir los detalles de la cuenta:

Captura de pantalla de la página Nerd Dinner Create a New Account (Crear una nueva cuenta).

Al hacer clic en el botón "Registrar", se creará un nuevo usuario en el sistema de pertenencia ASP.NET y se autenticará al usuario en el sitio mediante la autenticación de formularios.

Cuando un usuario ha iniciado sesión, Site.master cambia la parte superior derecha de la página para generar un mensaje "Welcome [username]!" y representa un vínculo "Cerrar sesión" en lugar de "Iniciar sesión". Al hacer clic en el vínculo "Cerrar sesión" se cierra la sesión del usuario:

Captura de pantalla de la página de formulario Nerd Dinner Host a Dinner. Los botones Bienvenida y Cerrar sesión se resaltan en la esquina superior derecha.

La funcionalidad de inicio de sesión, cierre de sesión y registro anterior se implementa dentro de la clase AccountController que Visual Studio agregó al proyecto cuando creó el proyecto. La interfaz de usuario de AccountController se implementa mediante plantillas de vista dentro del directorio \Views\Account:

Captura de pantalla del árbol de navegación Nerd Dinner. El punto c del controlador de cuenta está resaltado. También se resaltan los elementos de menú y carpeta de cuentas.

La clase AccountController usa el sistema de autenticación de formularios de ASP.NET para emitir cookies de autenticación cifradas y la API de ASP.NET Membership para almacenar y validar nombres de usuario y contraseñas. La API de pertenencia ASP.NET es extensible y permite usar cualquier almacén de credenciales de contraseña. ASP.NET incluye implementaciones integradas del proveedor de pertenencia que almacenan el nombre de usuario o las contraseñas dentro de una base de datos SQL o dentro de Active Directory.

Podemos configurar qué proveedor de pertenencia debe usar nuestra aplicación NerdDinner abriendo el archivo "web.config" en la raíz del proyecto y buscando la <sección de pertenencia> dentro de él. El web.config predeterminado agregado cuando el proyecto se creó registra el proveedor de pertenencia a SQL y lo configura para usar una cadena de conexión denominada "ApplicationServices" para especificar la ubicación de la base de datos.

La cadena de conexión predeterminada "ApplicationServices" (que se especifica en la <sección connectionStrings> del archivo web.config) está configurada para usar SQL Express. Apunta a una base de datos DE SQL Express denominada "ASPNETDB. MDF" en el directorio "App_Data" de la aplicación. Si esta base de datos no existe la primera vez que se usa la API de pertenencia dentro de la aplicación, ASP.NET creará automáticamente la base de datos y aprovisionará el esquema de base de datos de pertenencia adecuado dentro de ella:

Captura de pantalla del árbol de navegación Nerd Dinner. Los datos de la aplicación se expanden y se selecciona A S P NET D B dot M D F.

Si en lugar de usar SQL Express quisiéramos usar una instancia completa de SQL Server (o conectarnos a una base de datos remota), todo lo que necesitaríamos hacer es actualizar la cadena de conexión "ApplicationServices" dentro del archivo web.config y asegurarse de que el esquema de membresía adecuado esté agregado a la base de datos a la que apunta. Puede ejecutar la utilidad "aspnet_regsql.exe" en el directorio \Windows\Microsoft.NET\Framework\v2.0.50727\ para agregar el esquema adecuado para la pertenencia y los demás servicios de aplicación de ASP.NET a una base de datos.

Autorización de la dirección URL /Dinners/Create mediante el filtro [Authorize]

No teníamos que escribir ningún código para habilitar una implementación segura de autenticación y administración de cuentas para la aplicación NerdDinner. Los usuarios pueden registrar nuevas cuentas con nuestra aplicación e iniciar sesión o cerrar sesión en el sitio.

Ahora podemos agregar lógica de autorización a la aplicación y usar el estado de autenticación y el nombre de usuario de los visitantes para controlar lo que pueden y no pueden hacer en el sitio. Comencemos agregando lógica de autorización a los métodos de acción "Crear" de nuestra clase DinnersController. En concreto, es necesario que los usuarios que accedan a la dirección URL /Dinners/Create deben iniciar sesión. Si no han iniciado sesión, los redirigiremos a la página de inicio de sesión para que puedan iniciar sesión.

Implementar esta lógica es bastante fácil. Todo lo que necesitamos hacer es agregar un atributo [Authorize] a nuestros métodos de acción Crear de la siguiente manera:

//
// GET: /Dinners/Create

[Authorize]
public ActionResult Create() {
   ...
} 

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinnerToCreate) {
   ...
}

ASP.NET MVC admite la capacidad de crear "filtros de acción" que se pueden usar para implementar lógica que se pueda reutilizar que se pueda aplicar mediante declaración a los métodos de acción. El filtro [Authorize] es uno de los filtros de acción integrados proporcionados por ASP.NET MVC y permite al desarrollador aplicar mediante declaración reglas de autorización a métodos de acción y clases de controlador.

Cuando se aplica sin ningún parámetro (como arriba), el filtro [Authorize] exige que el usuario que realiza la solicitud del método de acción se debe iniciar sesión y redirigirá automáticamente el explorador a la dirección URL de inicio de sesión si no lo están. Al realizar esta redirección, la dirección URL solicitada originalmente se pasa como un argumento querystring (por ejemplo: /Account/LogOn? ReturnUrl=%2fDinners%2fCreate). A continuación, AccountController redirigirá al usuario a la dirección URL solicitada originalmente una vez que inicie sesión.

El filtro [Autorizar] admite opcionalmente especificar una propiedad "Usuarios" o "Roles" que se puede usar para requerir que el usuario haya iniciado sesión y esté dentro de una lista de usuarios permitidos o sea miembro de un rol de seguridad permitido. Por ejemplo, el código siguiente solo permite que dos usuarios específicos, "scottgu" y "billg", accedan a la dirección URL /Dinners/Create:

[Authorize(Users="scottgu,billg")]
public ActionResult Create() {
    ...
}

La inserción de nombres de usuario específicos dentro del código tiende a ser bastante no fácil de mantener. Un mejor enfoque consiste en definir "roles" de nivel superior que el código comprueba y, a continuación, asignar usuarios al rol mediante una base de datos o un sistema de Active Directory (lo que permite almacenar la lista de asignación de usuarios real externamente desde el código). ASP.NET incluye una API de administración de roles integrada, así como un conjunto integrado de proveedores de roles (incluidos los de SQL y Active Directory) que pueden ayudar a realizar esta asignación de roles o usuarios. Después, podríamos actualizar el código para permitir que los usuarios de un rol "administrador" específico accedan a la dirección URL /Dinners/Create:

[Authorize(Roles="admin")]
public ActionResult Create() {
   ...
}

Uso de la propiedad "User.Identity.Name" al programar cenas

Podemos recuperar el nombre de usuario del usuario que ha iniciado sesión actualmente de una solicitud mediante la propiedad User.Identity.Name expuesta en la clase base Controller.

Anteriormente, cuando implementamos la versión HTTP-POST del método de acción Create(), habíamos definido de manera fija la propiedad "HostedBy" del Dinner en una cadena estática. Ahora podemos actualizar este código para usar la propiedad User.Identity.Name, así como agregar automáticamente una confirmación de asistencia para el anfitrión que organiza la cena.

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {
    
        try {
            dinner.HostedBy = User.Identity.Name;

            RSVP rsvp = new RSVP();
            rsvp.AttendeeName = User.Identity.Name;
            dinner.RSVPs.Add(rsvp);

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Dado que hemos agregado un atributo [Authorize] al método Create(), ASP.NET MVC garantiza que el método de acción solo se ejecute si el usuario que visita la dirección URL /Dinners/Create ha iniciado sesión en el sitio. Por lo tanto, el valor de la propiedad User.Identity.Name siempre contendrá un nombre de usuario válido.

Uso de la propiedad User.Identity.Name en la edición de cenas

Ahora vamos a añadir cierta lógica de autorización que limite a los usuarios para que solo puedan editar las propiedades de las cenas que ellos mismos organizan.

Para ayudar con esto, primero agregaremos un método auxiliar "IsHostedBy(username)" a nuestro objeto Dinner (dentro de la Dinner.cs clase parcial que compilamos anteriormente). Este método auxiliar devuelve true o false en función de si un nombre de usuario proporcionado coincide con la propiedad Dinner HostedBy y encapsula la lógica necesaria para realizar una comparación de cadenas que no distingue mayúsculas de minúsculas:

public partial class Dinner {

    public bool IsHostedBy(string userName) {
        return HostedBy.Equals(userName, StringComparison.InvariantCultureIgnoreCase);
    }
}

A continuación, agregaremos un atributo [Authorize] a los métodos de acción Edit() dentro de nuestra clase DinnersController. Esto garantizará que los usuarios deben iniciar sesión para solicitar una dirección URL /Dinners/Edit/[id].

A continuación, podemos agregar código a nuestros métodos Edit que usan el método auxiliar Dinner.IsHostedBy(username) para comprobar que el usuario que ha iniciado sesión coincide con el host de Dinner. Si el usuario no es el host, mostraremos una vista "InvalidOwner" y finalizaremos la solicitud. El código para hacerlo tiene el siguiente aspecto:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    return View(new DinnerFormViewModel(dinner));
}

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (!dinner.IsHostedBy(User.Identity.Name))
        return View("InvalidOwner");

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new {id = dinner.DinnerID});
    }
    catch {
        ModelState.AddModelErrors(dinnerToEdit.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

A continuación, podemos hacer clic con el botón derecho en el directorio \Views\Dinners y elegir el comando de menú Agregar> vista para crear una nueva vista "InvalidOwner". Se rellenará con el siguiente mensaje de error:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    You Don't Own This Dinner
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Error Accessing Dinner</h2>

    <p>Sorry - but only the host of a Dinner can edit or delete it.</p>

</asp:Content>

Ahora, cuando un usuario intente editar una cena que no le pertenece, recibirá un mensaje de error:

Captura de pantalla del mensaje de error en la página web Nerd Dinner.

Podemos repetir los mismos pasos para los métodos de acción Delete() dentro de nuestro controlador para bloquear también el permiso para eliminar Cenas y asegurarnos de que solo el anfitrión de una Cena pueda eliminarla.

Estamos vinculando al método de acción Editar y eliminar de nuestra clase DinnersController desde nuestra dirección URL de detalles:

Captura de pantalla de la página Cena Nerd. Los botones Editar y Eliminar se encuentran en círculos en la parte inferior. Los detalles U R L se circunscriben en la parte superior.

Actualmente estamos mostrando los vínculos de acción Editar y Eliminar independientemente de si el visitante a la URL de detalles es el anfitrión de la cena. Vamos a cambiar esto para que los vínculos solo se muestren si el usuario que visita es el propietario de la cena.

El método de acción Details() dentro de nuestro DinnersController recupera un objeto Dinner y, a continuación, lo pasa como el objeto de modelo a nuestra plantilla de vista:

//
// GET: /Dinners/Details/5

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (dinner == null)
        return View("NotFound");

    return View(dinner);
}

Podemos actualizar nuestra plantilla de vista para mostrar u ocultar condicionalmente los vínculos Editar y Eliminar mediante el método auxiliar Dinner.IsHostedBy() como se indica a continuación:

<% if (Model.IsHostedBy(Context.User.Identity.Name)) { %>

   <%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID }) %> |
   <%= Html.ActionLink("Delete Dinner", "Delete", new {id=Model.DinnerID}) %>    

<% } %>

Pasos siguientes

Veamos ahora cómo podemos habilitar a los usuarios autenticados para confirmar su asistencia a cenas mediante AJAX.