Руководство SimpleMembership в ASP.NET MVC 4

  • Tutorial
С появлением ASP.NET MVC 4 и WebMatrix команда mvc стремиться сделать вещи проще для разработчика. Исходя из отзывов одно из направлений для улучшения была выбрана безопасность asp.net. 

ASP.NET MVC 4 шаблон проекта Internet добавил несколько новых очень полезных функций, которые построены с использованием SimpleMembership. SimpleMembership принес простую настройку ролей и пользователей, а также добавил поддержку OAuth. Однако новый провайдер не совместим с существующим ASP.NET Membership Provider.

В этом посте я расскажу о том, что такое SimpleMembership и как его использовать в проекте ASP.NET MVC 4.


Что такое SimpleMembership


  • SimpleMembership разработан для замены предыдущей версии ASP.NET Membership Provider (ASP.NET Role)
  • SimpleMembership решает те же самые задачи, но делает их проще для разработчика и приспосабливается к современным требованиям обеспечения безопасности
  • Контроллер AccountController в шаблоне проекта Interner ASP.NET MVC 4 требует SimpleMembership и не совместим с использованем старых версий
  • Досадная новость в том, что Web Site Administration Tool (WSAT) не совместим с SimpleMembership.

Система ASP.NET Membership System была представлена в 2005 году. Она была разработана для решения общих задач, таких как регистрации на сайте с использованием связки логина и пароля, хранения профиля в базе данных SQL Server. Также были добавлены механизмы расширения, позволяющие переопределить стандартную логику работы MembershipProvider и RoleProvider. 8 лет назад этот механизм справлялся со своей задачей, но сегодня это неудобный инструмент. Если профилю пользователя требуются дополнительные поля, то все они хранятся в одной колонке и для доступа требуют вызовов API соответствующего провайдера.

В ASP.NET WebPages и WebMatrix по новому представлены многие вещи, например движок представлений Razor и SimpleMembership.

Почему использование ASP.NET Membership не рекомендуется


Стандартный провайдер прекрасно работает, если выполняются следующие условия: вся информация будет храниться в полной версии базы данных SQL Server и то, что все необходимые данны представлены в виде набора атрибутов (UserName, Password, IsApproved, CreationDate...) и другая информация будет предоставлена используя Profile Provider. 

Требуется полный SQL Server по умолчанию


Большинство полнофункциональных провайдеров ASP.NET требуют полную версию SQL Server (т.к. опираются на работу хранимых процедур, на кеш SQL Server и другие возможности сервера). Кроме того, провайдеры по умолчанию не будут работать на SQL Azure.

Трудности работы с другой базой данных


Для работы с базой данных отличной от SQL Server необходимо переопределить набор методов провайдера, которые сильно ориентированы на хранение данных в реляционной базе данных. Во-первых это большой объем работы по переопределению этих методов, а во-вторых скорее всего останется множество неопределенных методов, содержащих System.NotImplementedException, что не красит код.

Ориентация на модель Пользователь > Роль


Существующие поставщики данных строго ориентированы на эту модель, в которой пользователь имеет логин и пароль. Конечно, дополнительные сведения могут быть добавлены через API, но такая модель не подходит для OAuth (там пользователь не имеет пароля). 

Система ориентирована на роли не всегда будет уместна, бывает удобнее использовать модель прав доступа к отдельным объектам или действиям (Claims)

Также требуется жестка схема базы данных с большим количеством blob колонок.

SimpleMembership улучшенная система пользователей


SimpleMembership был разработан как раз для решения проблем, озвученных выше. 

Matthew Osborn в своей записи Using SimpleMembership With ASP.NET WebPages объясняет, что SimpleMembership разработан, чтобы легко интегрироваться с вашей базой данных.

SimpleMembership требует, чтобы было 2 колонки в таблице пользователей: «ID» и «UserName». Важная часть здесь, что эти колонки могут иметь любые названия.


Схема таблицы пользователи

Теперь необходимо рассказать об этом SimpleMembership: добавить строку подключения к базе данных.
<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=ARCTURUS\SQLEXPRESS;Initial Catalog=MembershipDemoDB;Integrated Security=True;Pooling=False" providerName="System.Data.SqlClient" />
  </connectionStrings>

И определить инициализацию:
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "Id", "Name", autoCreateTables: true);


После запуска сайта и обработки атрибута инициализации SimpleMembership сам создат необходимые ему таблицы для работы. В качестве таблицы пользователей будет использоваться наша таблица Users.

SimpleMembership создал таблицы

SimpleMembership работает со всей линейкой SQL Server (SQL Azure, SQL Server CE, SQL Server Express и LocalDB). Все реализовано как вызовы SQL, что здесь гораздо лучше, чем использование хранимых процедур.

Использование EntityFramework с Code First


Проблема с ASP.NET Membership в том, что она хранит дополнительную информацию об аккаунте сама. Это значит, что нельзя напрямую получить доступ к данным профиля. В том время как SimpleMembership все равно, в какой таблице и как хранятся данные пользователя. Можно легко изменить таблицу с пользователями, например добавить адрес.

[Table("Users")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }

    public string Address { get; set; }
}


Теперь можно легко получить доступ к этому полю не используя сам провайдер, а напрямую из базы данных. Это позволяет принимать SimpleMembership как прослойку между базой данных и системой ASP.NET Membership.

Как он реализован я опущу, можно посмотреть в оригинальной записи SimpleMembership. Важно знать, что SimpleMembership унаследован от ExtendedMembershipProvider.

ASP.NET MVC 4 Internet template


В шаблоне по умолчанию реализован следующий механизм работы с SimpleMembership:

ASP.NET MVC 4 Internet template

  • AccountModel.cs описывает базовый аккаунт пользователя и включает атрибуты для базы данных
  • InitializeSimpleMembershipAttribute.cs как раз содержит информацию для инициализации провайдера: какую базу данных использовать, какие поля и другие настройки.
  • AccountController.cs содержит вызовы WebSecurity класс библиотеки WebMatrix.

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

Настройка SimpleMembership


Добавление SimpleMembership в существующий проект


Хотя этот провайдер работает по умолчанию в проекте с шаблоном ASP.NET MVC 4 Internet, некоторые (и я в том числе) создают проект с пустого шаблона. Необходимо добавить 2 ссылки: WebMatrix.Data и WebMatrix.WebData. Или установить эти библиотеки через NuGet с одноименными идентификаторами).

WebMatrix data

Теперь необходимо добавить провайдера и указать на использование WebMatrix

 <roleManager enabled="true" defaultProvider="SimpleRoleProvider">
      <providers>
        <clear />
        <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider,WebMatrix.WebData" />
      </providers>
    </roleManager>
    <membership defaultProvider="SimpleMembershipProvider">
      <providers>
        <clear />
        <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
      </providers>
    </membership>


Так как WSAT использовать не получится, есть 2 способа создания пользователей и ролей. Если используется модель EntityFramework Code First, удобно будет добавить Micragion с созданием пользователей по умолчанию:

public partial class AddDefaultUser : DbMigration
{
    public override void Up()
    {
        if (!WebSecurity.Initialized)
        {
            WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "Id", "UserName",
                autoCreateTables:
                    true);
        }

        var roles = (SimpleRoleProvider)Roles.Provider;
        var membership = (SimpleMembershipProvider)Membership.Provider;

        if (!roles.RoleExists("Admin"))
        {
            roles.CreateRole("Admin");
        }
        if (membership.GetUser("Admin", false) == null)
        {
            membership.CreateUserAndAccount("Admin", "SuperAdminPassword");
        }
        if (!roles.GetRolesForUser("Admin").Contains("Admin"))
        {
            roles.AddUsersToRoles(new[] { "Admin" }, new[] { "Admin" });
        }

    }

    public override void Down()
    {
        throw new NotImplementedException();
    }
}


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

Ссылки


Share post

Comments 11

    +4
    Хорошая, статья, спасибо.
    Но я бы не стал так категорично привязываться к MVC4 — в MVC5 beta 2 которого с чуть больше, чем неделю назад, обновилась до rc1, авторизация в которой меняется на Owin, более того, из местных плюшек там можно сразу заметить полезность вынесения изменения модели User прямо в классе модели,, который в MVC4 был, кхм, запрятан. Соответственно упрощается модификация модели пользователя и и таблиц, связанных с моделью пользовательского профиля, в прошлой версии для добавления свойств к пользовательскому профилю надо было повозиться, а в MVC5 все просто:

    public class User: IUser
    {
    public User()
    : this(String.Empty)
    {
    }

    public User(string userName)
    {
    UserName = userName;

    Id = Guid.NewGuid().ToString();
    UserGroupId = Id; // кастомной поле, добавленное мной
    UserRegisteredDate = DateTime.UtcNow; // кастомное поле, добавленное мной
    }
    [Key, HiddenInput(DisplayValue = false)] // помоему я спрятал
    public string Id { get; set; }

    [Required, Display(Name = «Имя входа в систему»)] // тоже моя вставка
    public string UserName { get; set; }
    }

    Из умолчательного кода инициализатора заметно, что вместо int в идентификаторах пользователя теперь используются более удобные (U|G)UID'ы, приведённые в строку. Роли теперь имеют не Id, RoleName, а id = RoleName, т.е состоят из одного поля string.
    Так же, изменена работа с провайдерами авторизации, код выкладывать лениво, но модели авторизации поменялись, как по мне, в хорошую сторону.
    «фильтрующий» класс убран, есть только модель и контроллер.

    Из минусов замечу, что работа с «профилем» текущего пользователя в rc1 либо отсутствует по причине rc, либо вынесена на откуп программисту.
    Например, в mvc4 в пользовательском «профиле» были замечательные поля — дата последнего входа, количество попыток ввести пароль, и с десяток других удобных по умолчанию, сейчас у меня это частично реализовано руками — feedback я уже по этому поводу отправил, жду ответа от команды разработки.

    Это касаемо авторизации
      0
      Спасибо большое за комментарий. Однако авторизация, для меня это не то место, где нужно гнаться за новыми плюшками, буду использовать как только буду понимать как она работает.

      Много где использую свою авторизацию (также описана в статье Авторизация ASP.NET MVC). Там мой профиль да и авторизация очень простая получается, выдать токен авторизации и при желании удалить его. Есть у вас мысли по этому способу?
        +1
        Это не новые плюшки — это авторизация в MVC5 по умолчанию, и придётся переделывать код проекта, и менять зависимости…
        Я вкратце расписал её плюсы и минусы.
        По поводу мыслей — кнопочки авторизоваться через fb/twi/google/ms account некоторым проще.
        Главное предусмотреть возможность отвязки аккаунта сайта от аккаунта социалки, и эта возможность тут тоже есть? как и в mvc4, насколько я помню код.
        Опять же, родная интеграция с Twitter Bootstrap (в rc уже 3 версия, кстати, можно обновиться), асинхронность некоторых методов, новый EF (в 2012 студии работа с SQL начала выдавать ошибку несовпадения версий библиотек доступа, да), какие-то еще мелочи, которые я навскидку не помню.
          0
          Авторизация на сайте это такое место, которое делается хорошо и редко меняется. Т.к. за безопасность пользовательских данных отвечают несколько строчек кода. И менять системы авторизации с каждым выходом фреймворка глупо. Другое дело если это можно сделать безболезненно и безопасно. В случае с SimpleMembership такой фокус не пройдет (если ранее использовался ASP.NET Membership).

          MVC5 пока не пробовал.
            0
            «Несколько строчек кода», простите… в mvc4 DonNetOpenAuth библиотеки, на секундочку, вместе занимают 2,5 мегабайта в проекте…
              0
              Об этом и говорю, что за капотом большая работа происходит, а использовать это всего написав несколько строчек. Ведь если переписывать систему авторизации совсем вручную, начиная с токенов, шифрования и пр — то с первого раза не получится, на помощь и приходят уже готовые и оттестированные системы.
                0
                … оттестированные сообществом и более профессиональными программистами, разбирающимися в безопасности
      0
      Вы так категорично пишете, что у меня стойкое ощущение, что я отстал от жизни. Давайте для начала разберем о чем речь. То, что вы называете «Система ASP.NET Membership System» — я так полагаю, имеется ввиду набор поставщиков Sql Server, в которые входят

      — Членство (класс SqlMembershipProvider).
      — Управлением ролями (класс SqlRoleProvider).
      — Профиль (класс SqlProfileProvider).
      — Персонализация веб-частей (класс SqlPersonalizationProvider).
      — Веб-события (класс SqlWebEventProvider).

      Хочется также отметить, что абсолютно необязательно использовать все эти поставщики одновременно, — можно использовать, к примеру, только членство, а управление ролями переделать под другое хранилище. Также нет необходимости весь этот огород инициализировать в базе — есть же средство регистрации, у него куча параметров. По поводу «стандартной логики работы MembershipProvider и RoleProvider» — по моему это были абстрактные классы, а то, что Вы имеете ввиду — уже SqlMembershipProvider и SqlRoleProvider.

      Теперь по поводу минусов.
      Требуется полный SQL Server по умолчанию
      У меня на Sql Server Web 2008 абсолютно спокойно ставятся все провейдеры и работают. В 2012 версии, я думаю, проблем тоже быть не должно. Наверное, Вы имели ввиду, что это не будет рабоать, например, с Sql CE?
      все необходимые данны представлены в виде набора атрибутов (UserName, Password, IsApproved, CreationDate...) и другая информация будет предоставлена используя Profile Provider
      Насколько я помню, за это отвечает Membership (MembershipUser)
      провайдеры по умолчанию не будут работать на SQL Azure
      — к сожалению, не могу это опровергнуть, ибо не сведущ. Но мне не понятно, почему не будут?
      Для работы с базой данных отличной от SQL Server необходимо переопределить набор методов провайдера
      Во-первых, есть уже много готовых, например Oracle, db2, но можно спокойно писать и кастомный, о чем есть инфа в документации.
      Ориентация на модель Пользователь > Роль
      — даже не понял, о чем речь, ес честно. Ну, не нравится стандартная, используйте свою. Не вижу проблем никаких. Можно даже свой ролепровайдер написать.
      Проблема с ASP.NET Membership в том, что она хранит дополнительную информацию об аккаунте сама. Это значит, что нельзя напрямую получить доступ к данным профиля
      Ибо мембершип и профиль разделены и не связаны друг с другом. И что значит, получить доступ напрямую? И зачем?
      Как он реализован я опущу, можно посмотреть в оригинальной записи SimpleMembership. Важно знать, что SimpleMembership унаследован от ExtendedMembershipProvider
      ExtendedMembershipProvider унаследован от того же MembershipProvider, от которого унаследован SqlMembershipProvider. Принципы работы у них одинаковые. Они просто хранят данные по разному.

      И напоследок.
      SimpleMembership разработан для замены предыдущей версии ASP.NET Membership Provider (ASP.NET Role)
      Я думаю, что они просто добавили новую реализацию мембершипа, которая решает самые простые задачи, от того и simple. И почему Вы пишете, что этот Simple может быть заменой стандартному — тоже не ясно, ведь это просто альтернативная реализация.
        0
        Спасибо за столь подробный отзыв. По порядку:

        Да, в основном рассматриваю все-же использование MS SQL + конечно же роли пользователя и сами профили пользователей, т.к. это наиболее распространенная задача (регистрация, авторизация).

        У меня на Sql Server Web 2008 абсолютно спокойно ставятся все провейдеры и работают. В 2012 версии, я думаю, проблем тоже быть не должно. Наверное, Вы имели ввиду, что это не будет рабоать, например, с Sql CE?


        Да, для SQL CE, также и для SQL Azure (хотя в новых версиях, где-то видел что гораздо проще запустить, но проблемы есть)

        Насколько я помню, за это отвечает Membership (MembershipUser)


        Верно, только получить эти данные напрямую из базы сложнее чем просто SQL запрос.

        Во-первых, есть уже много готовых, например Oracle, db2, но можно спокойно писать и кастомный, о чем есть инфа в документации.


        Я и не говорю, что их нет. Скорее о том, что реализация будет сложной если писать отличную от «заранее заданной конфигурации»

        Ориентация на модель Пользователь > Роль
        — даже не понял, о чем речь, ес честно. Ну, не нравится стандартная, используйте свою. Не вижу проблем никаких. Можно даже свой ролепровайдер написать.


        Это о том, что идея этой системы в том что существуют существуют пользователи и существуют роли у этих пользователей. А бывает еще Claim based auth system, когда есть некие права на чтение/запись и пр. SimpleMembership, как я понимаю, делает шаг в сторону упрощения для программиста написания системы авторизации, однако все-же остается такой пробел.

        И что значит, получить доступ напрямую? И зачем?

        Существует большое количество задач, где основным объектом является профиль пользователя, соответственно выполнять операции с ним хочется напрямую с необходимыми полями в базе а не со всем объектом и напрямую через провайдер данных (Data Layer) а не через Auth Layer.

        Я думаю, что они просто добавили новую реализацию мембершипа, которая решает самые простые задачи, от того и simple. И почему Вы пишете, что этот Simple может быть заменой стандартному — тоже не ясно, ведь это просто альтернативная реализация.


        Написан для замены — чтобы заменить, потому что он проще в использовании, функциональнее и расширяем. Ведь именно это подразумевает слово замена, чтобы прекратить использовать старый и использовать новый, где очень много чего добавлено (OAuth из коробки).

        А как вы относитесь к ручной системе авторизации (http://oxozle.com/2013/08/10/avtorizaciya-asp-net-mvc)?
        0
        Я и не говорю, что их нет. Скорее о том, что реализация будет сложной если писать отличную от «заранее заданной конфигурации»
        Эм, то, что Вы можете добавить юзеру любое количество полей, сгенерить таблицу для него через Code First и получить, используя SimpleMembership — это называете отличиями от заранее заданной конфигурации? Или о чем речь? Это всё можно сделать и со стандартной только танцев больше — получить ИД юзера и добавить дополнительную таблицу «ИД-Нужные мне поля».

        Claim based auth system
        Не понял, причем тут SimpleRoleProvider — ибо это такая же реализация SqlRoleProvider. Даже не удивлюсь, если вдруг можно будет подружить SimpleRoleProvider с SqlMembershipProvider и наоборот (симпл мембершип с sql role). О каких конкретно шагах речь то идет?

        Существует большое количество задач, где основным объектом является профиль пользователя, соответственно выполнять операции с ним хочется напрямую с необходимыми полями в базе а не со всем объектом и напрямую через провайдер данных (Data Layer) а не через Auth Layer.
        Согласен. А ещё я могу использовать доменную авторизацию и подтягивать профиль через LDAP — но мы же это минусом не называем для SimpleMembership. У каждого своя задача и от этого выбирают инструменты. Если надо напрямую оперировать профилем прямо в запросах к базе(разные джойны, например), ну, используйте для этого подходящие решения.

        Написан для замены — чтобы заменить, потому что он проще в использовании, функциональнее и расширяем
        Ну, вообще он не покрывает все провайдеры, о которых ведем речь. Как, например, он работать будет, если захочется использовать веб части? Если веб эвенты захочется писать встроенным механизмом в базу? Вы просто намешали всё в одну кучу. Ну, допустим, пользуемся SimpleMembershipProvider и SimpleRoleProvider — что мешает паралельно использовать провайдер для профиля, веб эвентов и персонализации веб частей?

        А как вы относитесь к ручной системе авторизации (http://oxozle.com/2013/08/10/avtorizaciya-asp-net-mvc)?
        Ну, я сам не фанат SqlMembershipProvider, и в каждом конкретном случае нужно выбирать подходящее решение. Я видел разные реализации, начиная от сложной системы ролей/пользователей и заканчивая списком юзеров в файлике на сервере.

        П.С. Это ответ автору в тред выше. Напутал я с кнопками
          0
          В дополнение.
          Верно, только получить эти данные напрямую из базы сложнее чем просто SQL запрос.

          Если речь идет о MembershipUser

        Only users with full accounts can post comments. Log in, please.