- PVSM.RU - https://www.pvsm.ru -

Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту

Приветствую.
Сегодня мне бы хотелось рассказать в совсем небольшом уроке (уровень скорее для очень начинающих), как можно достаточно быстро и легко настроить аутентификацию [1] пользователей, а так же авторизацию [2] при их доступе к некоторому функционалу на Вашем сайте, используя штатные средства фреймворка MVC(4).

Вводная

Я сейчас пишу личный простенький сайт для учета и ведения расходов, доходов, напоминания о периодических платежах (жкх, кредиты, школа и т.п.) + аналитика (в основном диаграммы), поскольку меня и мою жену функциональность Google Docs устраивать перестала.
Соответственно, встал вопрос о том, как закрыть информацию, в данном случае финансового состояния семьи от посторонних глаз под аутентификацию а так же распределить роли доступа (авторизация) — что могут жена, ребенок, анонимные пользователи, а что может администратор глава семьи.

Небольшое предисловие - Причины, побудившие меня написать эту статью и немного заметок для начинающих

Проект создавался в рамках изучения C#, MVC4 — я новичок.
Я потратил несколько вечеров на поиск, возню с пользовательскими провайдерами и их настройкой, пока не понял, что весь этот код не нужен мне на данном этапе. Следствием стало переписывание статьи по когда-то вбитому мне в голову принципу — чем меньше изменений вносится в любой объект, будь то конфигурационный файл, документ или код на текущем уровне моих знаний либо представлений, тем потом будет проще. Возможно я упустил какие-то важные нюансы (я начинающий ), поэтому буду рад как критике, так и подсказкам аудитории.

Я считаю, что пользователь уже создал хотя бы один простой сайт на данном фреймворке, например, воспользовавшись базовой инструкцией по созданию простого каталога Ваших фильмов на фреймворке MVC4 [3] (у меня ушло около 20 минут).
Или же изучая MVC по второй, очень хорошей и более сложной инструкцией, по созданию онлайн магазина продажи музыкальных альбомов [4], (она касается MVC3, но, тем не менее, изучение MVC я рекомендую начинать с данной инструкции).

В процессе изучения второй инструкции я натолкнулся на проблемы, связанные с тем, что некоторые вещи в MVC4 изменились, по сравнению с MVC3, и брать устаревший код контроллера от предыдущей модели фреймворка и модели считаю плохой идеей, поэтому решил разобраться с этой задачей.

Предварительное условие:

При создании нового проекта MVC4 по умолчанию вверху, справа при открытии сайта есть небольшое меню из двух пунктов — «Регистрация» и «Выполнить вход».
Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту

Техническое задание:

«Активировать» и запустить на нашем сайте ролевую модель доступа с распределением функционала.

По умолчанию все, что нам надо, уже есть и работает, спасибо разработчикам, но кое что нам придется дописать самостоятельно.

Рабочая среда:

У меня стоят привычные мне русскоязычные варианты Visual Studio 2012 Express, .Net 4.5, SQL 2012 и MVC4 (а так же TFS2012 Express. Всё это живет на Windows 2008R2), в случае установки у Вас английской локализации названия, пункты меню и другие элементы интерфейса будут называться по другому, поэтому я по максимуму абстрагировался от скриншотов экрана.

Решение задачи

Подготовка хранилища

Я предпочитаю отделять данные приложения от авторизации, поэтому создал отдельную базу данных users,

  1. В каталоге App_Data надо создать новую, пустую базу данных, назовём её «users» для идентификации её содержимого.
  2. В нашем web приложении надо описать подключение к нашей новой базе данных, для этого надо открыть файл web.config, который находится в корневом каталоге нашего приложения, найти (обычно в самом начале файла), пункт, описывающий соединения с источниками данных. (я взял конфиг из уже работающего сайта, поэтому у Вас верным и совпадающим пунктом будет только имя соединения DefaultConnection).
    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)v11.0;Initial Catalog=aspnet-MyMoney-2132141343;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|users.mdf" providerName="System.Data.SqlClient" />
    <add name="payDBContext" connectionString="Data Source=(LocalDb)v11.0;Initial Catalog=aspnet-MyMoney-data;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|MyMoney.mdf" providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    Немного подробнее:
    В данном списке подключений у нас есть соединение по умолчанию «DefaultConnection», у нас оно будет использоваться только для хранения пользовательской информации, поэтому мы это менять не будем, все данные приложения будут храниться в другой базе данных, payDBContext.
    В настройках DefaultConnection мы меняем:

    1. Начальный каталог «Initial Catalog=aspnet-MyMoney-2132141343», это имя базы данных в момент прикрепления БД к серверу БД, поэтому в случае работы нескольких баз данных с этим одинаковым именем, могут быть неоднозначности
    2. пункт «AttachDBFilename=|DataDirectory|users.mdf», имя файла базы данных, где будет храниться информация о пользователях и ролях. Имя базы данных, созданной нами в каталоге App_Data, «users.mdf».

  3. Теперь можно запустить сайт, зайти и зарегистрировать, например двух пользователей Admin и User
  4. После чего в обозревателе баз данных прямо в студии открываем структуру нашей БД
    И получим нечто подобное

    Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту
  5. Открываем табличку «UserProfile» в режиме редактирования, фактически нас интересуют UserID для созданных ранее пользователей Admin и User.
  6. Открываем таблицу «webpages_Roles» в режиме заполнения, и вводим роль Admin, в колонку «RoleName», идентификатор заполнится автоматически, и вводим вторую роль «User». Запоминаем полученные для ролей идентификаторы.
    Открываем в режиме заполнения таблицу «webpages_UsersInRoles»
    и вводим в соответствующие поля ID пользователя и ID соответствующей ему роли, но с небольшим отличием:
    Мы хотим, чтобы Администратор входил как в роль администратора, так и в роль пользователя. Поэтому в этот раз строк будет 3.

    И получим нечто подобное

    Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту

    У меня за работу с финансовой информацией отвечает несколько контроллеров, поэтому в описании каждого контроллера (можно закрыть или наоборот, открыть отдельные методы) надо вставить соответствующую настройку доступа:

    [Authorize(Roles = "Admin")]
    

    namespace MyMoney.Controllers
    {
        [Authorize(Roles = "Admin")]
        public class catController : Controller
        {
    

    Или же вставить такую строку для входа в методы нескольких ролей

    [Authorize(Roles = "Admin, User")]
    

    Теперь я хочу в зависимости от роли пользователя немного изменить список меню.

    1. В каталоге /Views/Shared я создаю частичное представление (оно же Partial View) с названием "_Menu"
      исходный код, кстати, кто подскажет как его лучше оптимизировать, а то утянул

      @{
          
          var menus = new[]
                      {
                         new { LinkText="На главную", ActionName="Index",ControllerName="Home",Roles="All" },
                         new { LinkText="О себе", ActionName="About",ControllerName="Home",Roles="All" },
                         new { LinkText="Контакты", ActionName="Contact",ControllerName="Home",Roles="All" },
                         new { LinkText="Финансы", ActionName="Index",ControllerName="payments",Roles="Admin,User" },
                      };
      }
      
      <ul id="menu">
      @if (HttpContext.Current.User.Identity.IsAuthenticated)
      {
          String[] roles = Roles.GetRolesForUser();
          var links = from item in menus
                      where item.Roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                      .Any(x => roles.Contains(x) || x == "All")
                      select item;
          foreach (var link in links)
          {
              @: <li> @Html.ActionLink(link.LinkText, link.ActionName,link.ControllerName)</li>
          }
      }
      else{
          var links = from item in menus
                      where item.Roles.Split(new String[]{","},StringSplitOptions.RemoveEmptyEntries)
                      .Any(x=>new String[]{"All","Anonymous"}.Contains(x))
                      select item;
           foreach ( var link in links){
               @: <li> @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)</li>
           }
      }
      </ul>
      		

    2. Теперь надо его подключить в разметку /Views/Shared/_Layout.cshtml
      Ищем в файле _Layout.cshtml этот код

         <section id="login">
                          @Html.Partial("_LoginPartial")
                      </section>
                      <nav>
              <ul id="navlist">
                  <li class="first"><a href="@Url.Content("~")" id="current">Home</a></li>
                  <li><a href="@Url.Content("~/Store/")">Store</a></li>
                  <li>@{Html.RenderAction("CartSummary", "ShoppingCart");}</li>
                  <li><a href="@Url.Content("~/StoreManager/")">Admin</a></li>
              </ul>        
                      </nav>
      

      и меняем блок

      <nav> ... </nav>
      

      на

      <nav>
                  @Html.Partial("~/Views/Shared/_Menu.cshtml")
      </nav>
                  

    3. У меня есть один контроллер с коротким именем, использующий одно представление как меню, вот код представления.
      Набросал по быстрому, чтобы показать работу с ролями.

      код представления

      @{
          var menus = new[]
                      {
                         new { LinkText="Home", ActionName="Index",ControllerName="Home",Roles="All"  },
                         new { LinkText="About", ActionName="About",ControllerName="Home",Roles="Anonymous"  },
                         new { LinkText="Contact", ActionName="Contact",ControllerName="Home",Roles="Anonymous"  },
                         
                         new { LinkText="Добавить платёж", ActionName="Create",ControllerName="pay",Roles="Admin,User"  },
                         new { LinkText="Просмотр платежей", ActionName="Index",ControllerName="pay",Roles="Admin,User"  },
      
                         new { LinkText="Добавить категорию", ActionName="Create",ControllerName="cat",Roles="Admin"  },
                         new { LinkText="Просмотр категорий", ActionName="Index",ControllerName="cat",Roles="Admin,User"  },
                         
                         new { LinkText="Добавить пользователя", ActionName="Create",ControllerName="user",Roles="Admin"  },
                         new { LinkText="Просмотр пользователей", ActionName="Index",ControllerName="user",Roles="Admin"  },
                         
                         new { LinkText="Добавить тип", ActionName="Create",ControllerName="type",Roles="Admin"  },
                         new { LinkText="Просмотр типов", ActionName="Index",ControllerName="type",Roles="Admin"  },
                         //new { LinkText="", ActionName="",ControllerName="",Roles="Administrator"  },
                      };
      }
      
      @{
          ViewBag.Title = "Управление финансами";
      }
      
      <h2>Управление финансами</h2>
      
      <p>Вы: @User.Identity.Name</p>
      <p>Вы входите в группы:</p>
      <table>
          <tr>
              @{ foreach (string item in Roles.GetRolesForUser())
                 {
                     <li>@item</li>
                 }
              }
          </tr>
      </table>
      
      
      <ul id="list">
      
          @if (HttpContext.Current.User.Identity.IsAuthenticated)
          {
              String[] roles = Roles.GetRolesForUser();
              var links = from item in menus
                          where item.Roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                          .Any(x => roles.Contains(x) || x == "All")
                          select item;
              foreach (var link in links)
              {
              @: <li> @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)</li>
          }
          }
          else
          {
              var links = from item in menus
                          where item.Roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                          .Any(x => new String[] { "All", "Anonymous" }.Contains(x))
                          select item;
              foreach (var link in links)
              {
              @: <li> @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)</li>
           }
          }
      </ul>
      

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

    Анонимный вход

    Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту
    Вход с административной ролью

    Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту
    Вход с ролью пользователя

    Использование функционала фреймворка MVC4 для авторизации пользователей и использование ролевой модели доступа к сайту

    Замечания напоследок

    Единственный минус — надо самому дописывать методы для добавления/удаления пользовательских профилей — не лезть же каждый раз в таблички ролей, профилей и т.п.

    Второе, о чем я сейчас думаю — надо в свою БД финансов привязать получение данных текущего авторизованного пользователя, т.к. сейчас в гугль докс приходится руками выбирать, кто был инициатором расхода/дохода — а вводить два раза информацию глупо. А в идеале — получать авторизацию и из Windows сессии, если броузер IE.

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

    Спасибо за внимание, надеюсь я кому-то помог.

Автор: foxmuldercp

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/web-razrabotka/18788

Ссылки в тексте:

[1] аутентификацию: http://ru.wikipedia.org/wiki/%D0%90%D1%83%D1%82%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F

[2] авторизацию: http://ru.wikipedia.org/wiki/%D0%90%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F

[3] воспользовавшись базовой инструкцией по созданию простого каталога Ваших фильмов на фреймворке MVC4: http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4

[4] очень хорошей и более сложной инструкцией, по созданию онлайн магазина продажи музыкальных альбомов: http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-1

[5] Источник: http://habrahabr.ru/post/156443/