Защита приложений с помощью проверки подлинности и авторизации

от Корпорации Майкрософт

Загрузить PDF-файл

Это шаг 9 бесплатного руководства по приложению "NerdDinner" , в которых показано, как создать небольшое, но полное веб-приложение с помощью ASP.NET MVC 1.

На шаге 9 показано, как добавить проверку подлинности и авторизацию для защиты приложения NerdDinner, чтобы пользователи могли зарегистрироваться и войти на сайт, чтобы создать новые ужины, и только пользователь, который размещает ужин, может изменить его позже.

Если вы используете ASP.NET MVC 3, рекомендуем следовать руководствам по начало работы С MVC 3 или MVC Music Store.

NerdDinner, шаг 9. Проверка подлинности и авторизация

Прямо сейчас наше приложение NerdDinner предоставляет всем, кто посещает сайт, возможность создавать и редактировать сведения о любом ужине. Давайте изменим это так, чтобы пользователи должны были зарегистрироваться и войти на сайт, чтобы создать новые ужины, и добавить ограничение, чтобы только пользователь, который проводит ужин, смог изменить его позже.

Чтобы включить эту функцию, мы будем использовать проверку подлинности и авторизацию для защиты приложения.

Основные сведения о проверке подлинности и авторизации

Проверка подлинности — это процесс идентификации и проверки удостоверения клиента, обращаюющегося к приложению. Проще говоря, речь идет о том, "кто" является конечным пользователем, когда он посещает веб-сайт. ASP.NET поддерживает несколько способов проверки подлинности пользователей браузера. Для веб-приложений Интернета наиболее распространенный подход к проверке подлинности называется "Проверка подлинности на основе форм". Проверка подлинности с помощью форм позволяет разработчику создать HTML-форму входа в своем приложении, а затем проверить имя пользователя или пароль, которые пользователь отправляет в базе данных или другом хранилище учетных данных паролей. Если сочетание имени пользователя и пароля правильное, разработчик может попросить ASP.NET выдать зашифрованный ФАЙЛ COOKIE HTTP для идентификации пользователя в будущих запросах. Мы будем использовать проверку подлинности с помощью форм в приложении NerdDinner.

Авторизация — это процесс определения того, имеет ли пользователь, прошедший проверку подлинности, разрешение на доступ к определенному URL-адресу или ресурсу или на выполнение какого-либо действия. Например, в приложении NerdDinner мы хотим разрешить, чтобы только пользователи, выполнившие вход, могли получить доступ к URL-адресу /Dinners/Create и создать новые Dinners. Мы также хотим добавить логику авторизации, чтобы только пользователь, который размещает ужин, смог изменить его и запретить доступ к редактированию для всех остальных пользователей.

Проверка подлинности с помощью форм и AccountController

Шаблон проекта Visual Studio по умолчанию для ASP.NET MVC автоматически включает проверку подлинности с помощью форм при создании новых приложений ASP.NET MVC. Он также автоматически добавляет в проект предварительно созданную реализацию страницы входа в учетную запись, что упрощает интеграцию безопасности на сайте.

Сайт по умолчанию. master master странице отображается ссылка "Вход" в правом верхнем углу сайта, если пользователь, обращаюющийся к ней, не прошел проверку подлинности:

Снимок экрана: страница

Если щелкнуть ссылку "Войти", пользователь перейдет по URL-адресу /Account/LogOn :

Снимок экрана: страница входа в Nerd Dinner.

Посетители, которые еще не зарегистрировались, могут сделать это, щелкнув ссылку "Зарегистрировать", которая переведет их по URL-адресу /Account/Register и позволит им ввести сведения об учетной записи:

Снимок экрана: страница создания учетной записи в Nerd Dinner.

Нажатие кнопки "Зарегистрировать" приведет к созданию нового пользователя в системе членства ASP.NET и проверке подлинности пользователя на сайте с помощью проверки подлинности на основе форм.

Когда пользователь вошел в систему, сайт. master изменяет верхнюю правую часть страницы, чтобы вывести сообщение "Добро пожаловать [имя пользователя]!" и отображает ссылку "Выйти" вместо "Вход". Если щелкнуть ссылку "Выйти", пользователь выйдет из системы:

Снимок экрана: страница формы

Описанные выше функции входа, выхода и регистрации реализованы в классе AccountController, который был добавлен в наш проект Visual Studio при создании проекта. Пользовательский интерфейс для AccountController реализуется с помощью шаблонов представлений в каталоге \Views\Account:

Снимок экрана: дерево навигации Nerd Dinner. Выделена точка контроллера учетной записи c s. Также выделены папка учетной записи и пункты меню.

Класс AccountController использует систему ASP.NET Forms Authentication для выдачи зашифрованных файлов cookie проверки подлинности, а API членства ASP.NET для хранения и проверки имен пользователей и паролей. API членства ASP.NET расширяем и позволяет использовать любое хранилище учетных данных паролей. ASP.NET поставляется со встроенными реализациями поставщиков членства, которые хранят имена пользователей и пароли в базе данных SQL или в Active Directory.

Мы можем настроить, какой поставщик членства должно использовать приложение NerdDinner, открыв файл web.config в корне проекта и найдите <раздел членства> в нем. web.config по умолчанию, добавленный при создании проекта, регистрирует поставщик членства SQL и настраивает его на использование строки подключения с именем ApplicationServices для указания расположения базы данных.

Строка подключения ApplicationServices по умолчанию (указанная в <разделе connectionStrings> файла web.config) настроена для использования SQL Express. Он указывает на базу данных SQL Express с именем ASPNETDB. MDF" в каталоге приложения "App_Data". Если эта база данных не существует при первом использовании API членства в приложении, ASP.NET автоматически создаст базу данных и подготовит в ней соответствующую схему базы данных членства:

Снимок экрана: дерево навигации Nerd Dinner. Данные приложения развернуты и выбран параметр A S P NET D B B dot M D F.

Если вместо SQL Express мы хотели использовать полный экземпляр SQL Server (или подключиться к удаленной базе данных), все, что нам нужно сделать, это обновить строку подключения ApplicationServices в файле web.config и убедиться, что соответствующая схема членства была добавлена в базу данных, на которую она указывает. Вы можете запустить служебную программу aspnet_regsql.exe в каталоге \Windows\Microsoft.NET\Framework\v2.0.50727\, чтобы добавить соответствующую схему для членства и другие службы приложений ASP.NET в базу данных.

Авторизация URL-адреса /Dinners/Create с помощью фильтра [Авторизация]

Нам не нужно было писать код, чтобы включить безопасную реализацию проверки подлинности и управления учетными записями для приложения NerdDinner. Пользователи могут регистрировать новые учетные записи в нашем приложении, а также входить в систему или выходить на сайт.

Теперь мы можем добавить логику авторизации в приложение и использовать состояние проверки подлинности и имя пользователя посетителей, чтобы контролировать, что они могут и не могут делать на сайте. Начнем с добавления логики авторизации в методы действия "Создать" класса DinnersController. В частности, требуется, чтобы пользователи, обращаюющиеся к URL-адресу /Dinners/Create , входили в систему. Если они не вошли в систему, мы перенаправим их на страницу входа, чтобы они могли выполнить вход.

Реализовать эту логику довольно просто. Все, что нам нужно сделать, это добавить атрибут фильтра [Authorize] в наши методы действия Create следующим образом:

//
// GET: /Dinners/Create

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

//
// POST: /Dinners/Create

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

ASP.NET MVC поддерживает возможность создания "фильтров действий", которые можно использовать для реализации логики повторного использования, которую можно декларативно применять к методам действий. Фильтр [Авторизовать] является одним из встроенных фильтров действий, предоставляемых ASP.NET MVC, и позволяет разработчику декларативно применять правила авторизации к методам действий и классам контроллера.

При применении без каких-либо параметров (как описано выше) фильтр [Авторизовать] принудительно требует, чтобы пользователь, выполняющий запрос метода действия, был выполнен вход, и он автоматически перенаправляет браузер на URL-адрес входа, если это не так. При перенаправлении изначально запрошенный URL-адрес передается в качестве аргумента строки запроса (например, /Account/LogOn? ReturnUrl=%2fDinners%2fCreate). После этого AccountController перенаправит пользователя обратно на первоначально запрошенный URL-адрес после входа.

Фильтр [Авторизовать] при необходимости поддерживает возможность указать свойство "Пользователи" или "Роли", которое можно использовать, чтобы требовать, чтобы пользователь входил в систему и входил в список разрешенных пользователей или член разрешенной роли безопасности. Например, приведенный ниже код позволяет только двум конкретным пользователям, scottgu и billg, получить доступ к URL-адресу /Dinners/Create:

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

Внедрение конкретных имен пользователей в код, как правило, довольно не поддерживается. Лучшим подходом является определение "ролей" более высокого уровня, которые проверяет код, а затем сопоставление пользователей с ролью с помощью базы данных или системы Active Directory (что позволяет сохранить фактический список сопоставления пользователей из кода извне). ASP.NET включает встроенный API управления ролями, а также встроенный набор поставщиков ролей (в том числе для SQL и Active Directory), которые могут помочь выполнить сопоставление пользователей и ролей. Затем можно обновить код, чтобы разрешить доступ к URL-адресу /Dinners/Create только пользователям в определенной роли "администратор":

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

Использование свойства User.Identity.Name при создании ужинов

Мы можем получить имя пользователя, выполнившего вход в запрос, с помощью свойства User.Identity.Name, доступного в базовом классе Controller.

Ранее, когда мы реализовали версию HTTP-POST нашего метода действия Create(), мы жестко закодировали свойство HostedBy ужина в статическую строку. Теперь мы можем обновить этот код, чтобы вместо этого использовать свойство User.Identity.Name, а также автоматически добавить RSVP для узла, создающего Dinner:

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

Так как мы добавили атрибут [Authorize] в метод Create(), ASP.NET MVC гарантирует, что метод действия выполняется только в том случае, если пользователь, посещающий URL-адрес /Dinners/Create, вошел на сайт. Таким образом, значение свойства User.Identity.Name всегда будет содержать допустимое имя пользователя.

Использование свойства User.Identity.Name при редактировании ужинов

Теперь давайте добавим логику авторизации, которая ограничивает пользователей, чтобы они могли изменять только свойства ужинов, которые они сами размещают.

Чтобы помочь в этом, мы сначала добавим вспомогательный метод IsHostedBy(username)" в наш объект Dinner (в разделяемом классе Dinner.cs, который мы создали ранее). Этот вспомогательный метод возвращает значение true или false в зависимости от того, соответствует ли предоставленное имя пользователя свойству Dinner HostedBy, и инкапсулирует логику, необходимую для сравнения строк без учета регистра:

public partial class Dinner {

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

Затем мы добавим атрибут [Authorize] в методы действия Edit() в классе DinnersController. Это гарантирует, что пользователи должны войти в систему, чтобы запросить URL-адрес /Dinners/Edit/[id] .

Затем мы можем добавить код в методы Edit, которые используют вспомогательный метод Dinner.IsHostedBy(имя пользователя), чтобы убедиться, что вошедший пользователь соответствует узлу Dinner. Если пользователь не является ведущим, мы отобразим представление InvalidOwner и прервем запрос. Код для этого выглядит следующим образом:

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

Затем мы можем щелкнуть правой кнопкой мыши каталог \Views\Dinners и выбрать команду меню Добавить-вид>, чтобы создать новое представление InvalidOwner. Мы заполним его следующим сообщением об ошибке:

<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>

И теперь, когда пользователь пытается изменить ужин, который у них нет, он получит сообщение об ошибке:

Снимок экрана: сообщение об ошибке на веб-странице Nerd Dinner.

Мы можем повторить те же действия для методов действия Delete() в нашем контроллере, чтобы заблокировать разрешение на удаление dinners, и убедиться, что только хозяин ужина может удалить его.

Мы ссылаемся на метод действия "Изменить и удалить" класса DinnersController из URL-адреса сведений:

Снимок экрана: страница

В настоящее время мы показываем ссылки действие "Изменить" и "Удалить", независимо от того, является ли посетитель URL-адреса сведений хозяином ужина. Давайте изменим это так, чтобы ссылки отображались только в том случае, если пользователь является владельцем ужина.

Метод действия Details() в нашем DinnersController извлекает объект Dinner, а затем передает его в качестве объекта модели в наш шаблон представления:

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

public ActionResult Details(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

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

    return View(dinner);
}

Мы можем обновить шаблон представления для условного отображения или скрытия ссылок "Изменить" и "Удалить", используя вспомогательный метод Dinner.IsHostedBy(), как показано ниже:

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

<% } %>

Next Steps

Теперь давайте рассмотрим, как включить пользователей, прошедших проверку подлинности, в RSVP для ужинов с помощью AJAX.