2 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Использование Identity

Использование ASP.NET Identity

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

ASP.NET Identity – это API-интерфейс, используемый в веб-приложениях для управления данными пользователей, выполнения проверки подлинности и авторизации.

Большинство приложений требуют, чтобы пользователи создавали учетные записи и получали определенные учетные данные для доступа к контенту и услугам. ASP.NET Identity предоставляет средства для выполнения этих операций.

ASP.NET Identity не используется непосредственно в рамках MVC, но интегрируется через стандартные функции авторизации MVC.

Аутентификация в приложениях ASP.NET

Большинство web-сайтов работают в режиме анонимного доступа. Они содержат информацию, которую могут просматривать все желающие, и поэтому не проводят аутентификацию пользователей. Web-приложения ASP.NET предоставляют анонимный доступ к серверным ресурсам посредством назначения учетной записи анонимному пользователю. По умолчанию учетная запись для анонимного доступа имеет имя в виде IUSER _ имя компьютера.

ASP.NET исполняет web-приложения под учетной записью ASPNET. Это означает, что при выполнении задачи, не предусмотренной привилегиями пользователя (например, запись файла на диск), приложение получает отказ в доступе.
Идентификация пользователей применяется в тех случаях, когда нужно предоставить доступ к разделам web -приложения только для определенных пользователей. Это может быть Internet -магазины, форумы, закрытые разделы в корпоративных Intranet -сайтах и так далее.
Безопасность в приложениях ASP.NET основана на трех операциях:

  • Аутентификация – процесс идентификации пользователя для предоставления доступа к какому-то ресурсу приложения (разделу сайта, странице, базе данных, …). Аутентификация основана на проверке сведений о пользователе (например, имени и пароля);
  • Авторизация – процесс предоставления доступа пользователю на основе данных аутентификации;
  • Олицитворение (impersonalisation) – предоставление серверному процессу ASP.NET прав доступа клиента.

Существует три способа аутентификации пользователей в приложениях ASP.NET:

  • аутентификация Windows — применяется для идентификации и авторизации пользователей в зависимости от привилегий учетной записи пользователя. Работает аналогично обычным механизмам сетевой безопасности Windows и выполняется контроллером домена;
  • аутентификация Forms — пользователь вводит логин и пароль в Web -форме, после чего авторизация происходит по списку пользователей, хранящемуся, например, в базе данных. Применяется на большинстве Internet-сайтов при регистрации в Inernet -магазинах, форумах, пр;
  • аутентификация Passport — все пользователи имеют единое имя и пароль, используемые для сайтов, использующих данный тип авторизации. Пользователи регистрируются в службе Microsoft Passport.

Важно отметить, что аутентификация ASP.NET применяются только для web -форм (.aspx -файлы), контролов (.ascx -файды) и прочих ресурсов ASP.NET. HTML-файлы не входят в этот список. Для авторизации доступа к HTML -файлам нужно их зарегистрировать вручную!
Тип аутентификации указывается в конфигурационном файле Web.config :

По умолчанию применяется тип аутентификации Windows. Значение None имеет смысл устанавливать если используется собственная схема аутентификации или анонимный доступ (для повышения производительности).
Аутентификация Windows. Существует 4 типа аутентификации Windows : обычная ( basic ), краткая ( digest ), встроенная ( integated ) и на основе клиентских сертификатов SSL. Обычную и краткую аутентификацию применяют для идентификации имени пользователя и пароля, указываемом в диалоговом окне. Они хорошо работают в Internet , так как данные передаются по HTTP. Базовая аутентификация передает пароль и имя пользователя в кодировке Base 64, которую легко раскодировать. Для повышения безопасности можно использовать базовую аутентификацию совместно с SSL. Базовую аутентификация поддерживают большинство браузеров.
Краткая аутентификация является более безопасной, так как пароль шифруется по алгоритму MD 5. Она поддерживается браузерами Internet Explorer 5.0 и выше, либо на клиентской машине должен быть установлен. NET Framework. Кроме этого, учетные записи пользователей должны храниться в Active Directory.
Встроенная аутентификация применяется для идентификации учетных записей Windows и не может применяться в Internet , так как клиент и сервер должны пройти проверку контроллером домена. При этом пароли по сети не передаются, что увеличивает безопасность приложения. Этот тип аутентификации блокируется файрволами и работает только с Internet Explorer. Встроенная аутентификации немного медленнее, чем базовая или краткая.
Применение сертификатов SSL так же обычно применяется в Intranet , т.к. требует раздачи цифровых сертификатов. При этом типе аутентификации пользователям не нужно регистрироваться. Сертификаты можно сопоставить учетным записям пользователей в домене или Active Directory.

Для указания способа аутентификации нужно выполнить следующие действия:
1. Запустить диспетчер IIS
2. Щелкнуть правой кнопкой мыши по приложению и выбрать в контекстном меню Свойства.
3. В появившимся диалоге перейти на вкладку Безопасность каталога и нажать кнопку Изменить в разделе Анонимный доступ и проверка подлинности.

4. В диалоге Методы проверки подлинности указать тип аутентификации.

5. Указать права доступа к папке или отдельным файлам в папке Web -приложения. Обязательно нужно разрешить доступ для пользователя ASPNET.

Для поддержки URL-авторизации при Windows-аутентификации для защиты содержимого папок применяются Web.config файлы, находящиеся в этих папках. Структура файла такова (cимвол «*» означает всех пользователей):

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

Читать еще:  Что делать если наушники стали тихо играть

Если мы хотим защитить он неаутентифицированных пользователей папку полностью (например, папку, содержащую формы для администрирования сайта), то нужно разместить в ней файл Web.config с таким содержанием (cимвол «?» означает анонимных неавторизированных пользователей):

Если же мы хотим защитить только один файл (например, для подтверждения заказа в Internet -магазине), то в Web.config из корневой папки нужно добавить такие строки:

Приложение извлекает данные пользователей с помощью свойства Identity класса User. Это свойство возвращает объект, содержащий имя пользователя и роль.

bool authenticated = User.Identity.IsAuthenticated ;
string name = User.Identity.Name;
bool admin = User.IsInRole(«Admins»);

Forms-аутентификация

При использовании Forms-аутентификации запрос параметров регистрации (например, логина и пароля) происходит в web-форме. Регистрационная страница указывается в файле Web.config. При первом обращении к защищаемым страницам ASP.NET перенаправляет пользователя на страницу для ввода пароля. При успешной регистрации аутентификационные данные сохраняются в виде cookie и при повторном обращении к защищенным страницам регистрация не требуется.
Для того, чтобы использовать Forms-аутентификацию в файле Web.config в корневой папке приложения нужно указать страницу для ввода пароля:

При попытке просмотра защищенной страницы ASP.NET проверяет, есть ли аутентификационных cookie в запросе. Если cookie нет, то запрос перенаправляется на страницу для регистрации, если есть — ASP.NET дешифрует cookie и извлекает из него регистрационную информацию.

На форме находятся поля для ввода логина и пароля и флажок для сохраняемой регистрации. При нажатии кнопки «Войти» происходит поиск пользователя с таким логином и паролем. Если такой пользователь найден, вызывается функция FormsAuthentication.RedirectFromLoginPage (), в которой указывается идентификатор пользователя и флаг для сохраняемой регистрации. Если же нет – выводится сообщение об ошибке.

protected void btnLogin_Click(object sender, System.EventArgs e)
<
if (!IsValid) // проверяем правильность введенных данных
return;

OleDbConnection connection = GetDbConnection();

OleDbCommand command = new OleDbCommand(string.Format(«SELECT id FROM Customers WHERE login=’<0>‘ AND password=’<1>‘», login, password), connection);

OleDbDataReader reader = command.ExecuteReader();
if (!reader.Read()) // пароль или логин неверны
<
lblError.Text = «Неверный пароль – попробуйте еще раз»;
return ;
>

FormsAuthentication.RedirectFromLoginPage(id, chkbRememberLogin.Checked);
>
catch (OleDbException ex)
<
lblError.Text = «Ошибка базы данных»;
>
finally
<
connection.Close();
>
>

Аутентификации на основе ролей

Затем при каждом запросе нужно связывать учетные записи пользователей и роли. Обычно это делается в обработчике события AuthenticateRequest в файле Global.asax.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
<
HttpApplication appl = (HttpApplication)sender;

DataTable tblUsers = (DataTable)Application[«UsersTable»];
appl.Context.User = new GenericPrincipal(identity,
new string[] <(string)(tblUsers.Rows.Find(identity.Name)["Role"]) >);
>
>

В коде проверяется тип аутентификации пользователя и то, что он уже зарегистрирован. Имя пользователя извлекается из cookie свойством Name. Таблица с именами пользователей и их ролями для повышения быстродействия была сохранена в объекте Application. Из этой таблицы и находим роль пользователя, которую сохраняем в объекте GenericPrincipal.

Параметры аутентификации

Когда сеансовый cookie возвращается в следующих после регистрации запросах, он автоматически обновляется, если время жизни истекло больше чем на половину. Время же жизни сохраняемых cookie равно 50 годам.
Можно указать имя аутентификационных cookie , поместив его в атрибут name (имя по умолчанию — ASPXAUTH ):

По умолчанию аутентификацонные cookie шифруются и проверяются. Уровень защиты можно указать через атрибут protection , значение по умолчанию которого All. Значение Validation предписывает только проверку cookie , а значение Encript – только шифрование. Полностью отключить защиту можно указав значение None. Отключать защиту имеет смысл если данные передаются по протоколу HTTPS.

Сброс forms-аутентификации

Сброс регистрации можно увидеть на многих сайтах. Для сброса аутентификации применяется метод FormsAuthentication.SignOut (). Он устанавливает дату окончания действия cookie на прошедшее время и cookie автоматически уничтожается.

Аутентификация Passport

Для использования Passport аутентификации в web -приложении нужно установить Passport SDK. Passport SDK предоставляется бесплатно для тестирования, но для коммерческого использования на сайте необходимо приобретать лицензию.
При обращении к приложению с Passport аутентификацией проверяется наличие cookie с данные Passport. Если такого файла нет, пользователь перенаправляется на страницу для регистрации Passport.
Для включения данного режима аутентификации в файле Web. config нужно указать следующее:

Для обязательной регистрации всех посетителей сайта в разделе autorization нужно запретить доступ неавторизированным пользователем:

Обновить:

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

  • Шаг 1: Скачать PC Repair & Optimizer Tool (Windows 10, 8, 7, XP, Vista — Microsoft Gold Certified).
  • Шаг 2: Нажмите «Начать сканирование”, Чтобы найти проблемы реестра Windows, которые могут вызывать проблемы с ПК.
  • Шаг 3: Нажмите «Починить все», Чтобы исправить все проблемы.

2) Удалите .NET Framework (Примечание. Это предотвратит работу приложений, использующих .NET Framework):
— Откройте панель управления
— Откройте «Установка и удаление программ».
— Выберите Microsoft.NET Framework 1.1.
— Нажмите на Изменить / Удалить. Один из этих подходов полностью удаляет учетную запись из вашей системы.

Чтобы удалить эту учетную запись компьютера ASP.NET

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

  1. Нажмите Windows + R, чтобы открыть окно «Выполнить», введите regedit и нажмите Enter.
  2. В окне вкладки «Редактор» перейдите к следующему разделу реестра: HKEY_LOCAL_MACHINE Software Microsoft Windows NT CurrentVersion Winlogon SpecialAccounts Userlist. Если Специальный
  3. Ключ учетных записей и ключ списка пользователей не существуют, создайте их.
  4. В правой части списка пользователей щелкните правой кнопкой мыши в любом месте и укажите значение New -> DWORD (32-bit).
  5. Назовите новое имя параметра реестра DWORD точно так же, как имя пользователя, используемое на экране входа в систему. (В этом случае учетная запись компьютера ASP.NET)
  6. Дважды щелкните значение записи DWORD и установите значение 0, чтобы скрыть учетную запись перед экраном приветствия. Чтобы восстановить учетную запись пользователя на главном экране, удалите регистрационную запись или установите значение данных в 1.
  7. Выйдите из редактора реестра. Выйдите из системы или перезагрузите компьютер, чтобы изменения вступили в силу.
Читать еще:  Экран сместился влево что делать

Переустановка .NET Framework

Учетная запись создается из-за некоторых ошибок конфигурации во время первоначальной установки .NET Framework. Тем не менее, переустановка автоматически удаляет его. Поэтому мы рекомендуем вам загрузить фреймворк обратно на компьютер и попытаться установить его из исполняемого файла. Он скажет вам, что он уже установлен, и спросит, хотите ли вы его переустановить. Выберите Переустановить, и учетная запись будет автоматически удалена.

CCNA, веб-разработчик, ПК для устранения неполадок

Я компьютерный энтузиаст и практикующий ИТ-специалист. У меня за плечами многолетний опыт работы в области компьютерного программирования, устранения неисправностей и ремонта оборудования. Я специализируюсь на веб-разработке и дизайне баз данных. У меня также есть сертификат CCNA для проектирования сетей и устранения неполадок.

Рабочий процесс загружает ASP.NET ISAPI

Рабочий процесс “w3wp.exe” ищет URL запроса, чтобы загрузить нужное расширение ISAPI. Запрошенный ресурс в URL — “mypage.aspx”. Что происходит дальше? Полный разбор расширений ISAPI (и фильтров) не входит в статью, но вкратце, расширения ISAPI являются способом, посредством которого IIS обрабатывает запросы на разные ресурсы. После установки ASP.NET он устанавливает свое собственное расширение ISAPI (aspnet_isapi.dll) и добавляет привязку в IIS. IIS увязывает разные расширения с их расширениями ISAPI. Привязки в IIS можно посмотреть так: щелкнуть правой кнопкой мыши по веб-сайту-Свойства-вкладка Домашний каталог-кнопка Конфигурация-вкладка Привязки. Рисунок ниже показывает привязки:

Как видно, расширение “.aspx” увязано с расширением aspnet_isapi.dll. Рабочий процесс передает запрос расширению aspnet_isapi. Расширение aspnet_isapi, в свою очередь, загружает среду выполнения HTTP, и начинается обработка запроса.

Перед изучением того, что происходит внутри среды выполнения HTTP, рассмотрены детали того, как рабочий процесс загружает веб-приложение. Рабочий процесс загружает сборку веб-приложения, выделяя один домен приложения для приложения. Когда рабочий процесс запускает веб-приложение (в его домене приложения), веб-приложение наследует идентичность процесса (NetworkService по умолчанию), если перевоплощение запрещено. Но если перевоплощение разрешено, каждое веб-приложение работает под учетной записью, аутентифицированной IIS, или под учетной записью пользователя, настроенной в web.config.

• Identity impersonate=»true»
o Если IIS разрешает только анонимный доступ, переданной веб-приложению идентичностью будет [machine]IUSR_[machine].
o Если только встроенная аутентификация Windows разрешена в IIS, переданной веб-приложению идентичностью будет аутентифицированный пользователь Windows.
o Если разрешены встроенная аутентификация Windows и анонимный доступ, переданная веб-приложению идентичность будет зависеть от той, которая была аутентифицирована IIS. IIS сначала пытается использовать анонимный доступ, чтобы предоставить пользователю доступ к ресурсу веб-приложения. Если эта попытка проваливается, то он пытается использовать аутентификацию Windows
• Identity impersonate=»true» username=»username» password=»password»
o Позволяет веб-приложению работать под конкретной идентичностью.

Замечание: IIS 6.0 и IIS 5.0 по-разному обрабатывают запросы. Режим ядра http.sys реализован только в IIS 6.0. Такого свойства у IIS 5.0 нет. В IIS 5.0 запрос перехватывается непосредственно модулем aspnet_asapi, передающим запрос рабочему процессу. Рабочий процесс и модуль ISAPI взаимодействуют посредством конвейеров, приводя к издержкам вызова. Более того, один экземпляр рабочего процесса обслуживает все веб-приложения; нет пулов приложений. Модель IIS 6.0 намного лучше модули IIS 5.0. Рабочим процессом в IIS 5.0 является “aspnet_wp.exe”, в отличие от “w3wp.exe” в IIS 6.0. Рабочий процесс “aspnet_wp.exe” работает под стандартной учетной записью “ASPNET”, в отличие от “NetworkService” в IIS 6.0. Можно изменить эту учетную запись, найдя элемент

в файле “machine.config”.

Замечание: IIS 7.0 предоставляет два способа обработки запросов ASP.NET. Первый – классический способ, работающий так же, как в IIS 6.0; полезен для совместимости. Второй – новый комплексный способ, в котором ASP.NET и IIS входят в один и тот же конвейер обработки запроса. Во втором способе модули и обработчики .NET подключаются непосредственно к общему конвейеру обработки запроса, что эффективней способа IIS 6.0.

Скрытые поля на HTML-форме (hidden form fields)

Суть данного подхода состоит в том, что мы обеспечиваем навигацию по сайту при помощи стандартных html-форм. И при каждом следующем запросе мы сохраняем данные из предыдущего в скрытых полях на форме. Например:

В данном примере мы на первой html-форме получаем имя пользователя. Далее в контроллере в методе Forms2() мы извлекаем это значение из коллекции Form и передаем в представление посредством объекта ViewBag. В этом представлении генерируется код новой формы и в скрытом поле сохраняется имя пользователя. Таким образом, значение имени пользователя будет передано уже на третью формы вместе с дополнительной информацией — значением поля с именем «foodName». И так далее.

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

  • Во-первых, этот вариант не будет работать, если html-формы на наших страницах статичны, то есть жестко закодированы. И чтобы это исправить, чтобы повлиять на html-разметку мы прибегаем к помощи какой-нибудь серверной технологии (в данном случае механизм ViewBag);
  • Это безопасность. Хоть вводимые нами данные не передаются через url-параметры в адресной строке и визуально не видны на странице, мы с легкостью можем их получить или подменить или удалить или украсть просто изучив исходный код страницы или структуру запроса;

После успешной установки Web Deploy в консолии IIS должны появится разделы Management Service и Management Service Delegation . Для корректной работы Web Deploy в разделе Management Service следует разрешить удаленные подключения:

Здесь же можно указать SSL-сертификат, если у вас такой имеется.

Если вы все сделали правильно, то в контекстном меню каждого сайта должен появиться пункт меню Deploy :

Необходимо выбрать пункт меню Configure Web Deploy Publishing , чтобы разрешить публикацию для этого сайта, указать имя пользователя, от имени которого будут публиковаться пакеты, URL и нажать кнопку Setup .

Внимание: Важно, чтобы пользователь, которого вы указываете при настройке Web Deploy имел права на запись в папку веб-сайта.

Теперь всё готово для публикации приложения.

Настройка SPA клиента

При расположении клиента и сервера на разных origin требуется дополнительная настройка и на клиенте. Необходимо оборачивать каждый запрос использованием XMLHttpRequest.withCredentials.

Я обернул свои методы следующим образом:

Мы можем обернуть свой request config любым способом, главное, чтобы там был withCredentials = true.

Свойство XMLHttpRequest.withCredentials определяет, должны ли создаваться кросс-доменные запросы с использованием таких идентификационных данных, как cookie, авторизационные заголовки или TLS сертификаты.

Этот флаг также используется для определения, будут ли проигнорированы куки, переданные в ответе. XMLHttpRequest с другого домена не может установить cookie на свой собственный домен в случае, если перед созданием этого запроса флаг withCredentials не установлен в true.

Другими словами, если не указать этот атрибут, то наш cookie не сохранится браузером, т.е. мы не сможем обратно отправить cookie на сервер, а сервер не найдет желаемую cookie с JWT и не подпишет Bearer Token в нашем HTTP-pipeline.

Сброс базы данных

Когда вы запустите приложение и перейдете по адресу /Admin/Index, то увидите заметную задержку в несколько секунд перед откликом приложения. Это происходит потому, что Entity Framework в первый раз подключается к базе данных и определяет, что еще нет определенной схемы данных. Code-First использует определенные нами ранее классы (и еще некоторые стандартные классы Identity) для создания схемы базы данных.

Эффект можно увидеть если вы откроете в Visual Studio окно Server Explorer и настроите подключение к базе данных IdentityDB. Эта база данных будет содержать таблицы AspNetUsers и AspNetRoles.

Чтобы сбросить базу данных вы можете щелкнуть по ней правой кнопкой мыши в приложении Microsoft SQL Server Managment Studio и выбрать команду Delete. Для создания базы данных щелкните правой кнопкой мыши по узлу Data Connections в окне Server Explorer и выберите в контекстном меню команду Create New SQL Server Database. В открывшемся модальном окне задайте название для базы IdentityDB.

Теперь, когда вы снова запросите страницу /Admin/Index, Identity опять обнаружит что в база данных пустая и воссоздаст схему данных. Этот прием сброса базы данных может быть полезен на этапе тестирования возможностей Identity и позже мы будем использовать его не раз.

Аутентификация пользователей

Запросы к страницам с ограниченным доступом успешно перехватываются и перенаправляются контроллеру Account, но учетные данные, предоставляемые пользователем, пока не проверяются системой аутентификации. В примере ниже вы можете увидеть, как мы завершим процесс входа пользователей в систему:

Самая простая часть — это проверка учетных данных, которую мы делаем с помощью метода FindAsync класса AppUserManager:

В дальнейшем мы будем многократно обращаться к экземпляру класса AppUserManager, поэтому мы создали отдельное свойство UserManager, который возвращает экземпляр класса AppUserManager с помощью метода расширения GetOwinContext() класса HttpContext.

Метод FindAsync принимает в качестве параметров имя и пароль, введенные пользователем, и возвращает экземпляр класса пользователя (AppUser в примере) если учетная запись с такими данными существует. Если нет учетной записи с таким именем или пароль не совпадает, метод FindAsync возвращает значение null. В этом случае мы добавляем ошибку в метаданные модели, которая будет отображена пользователю. ClaimsIdentity, который идентифицирует пользователя. Класс ClaimsIdentity является частью ASP.NET Identity и реализует интерфейс IIdentity, который был описан ранее. Экземпляры ClaimsIdentity создаются путем вызова статического метода CreateIdentityAsync() класса UserManager. Этому методу передается объект пользователя (IdentityUser) и тип аутентификации в перечислении DefaultAuthenticationTypes.

Следующий шаг — удаление всех старых аутентифицирующих файлов cookie и создание новых. Мы добавили свойство AuthManager в контроллере Account, которое возвращает объект, реализующий интерфейс IAuthenticationManager, который отвечает за выполнение аутентификации в ASP.NET Identity. В таблице ниже перечислено несколько полезных методов этого интерфейса:

Вход пользователя в систему, что фактически означает создание аутентифицирующего cookie-файла

Выход пользователя из системы, который обычно означает удаление файлов cookie

Аргументами метода SignIn являются объект AuthenticationProperties, определяющий свойства настройки процесса аутентификации и объект ClaimsIdentity. Мы задали свойству IsPersistent объекта AuthenticationProperties значение true — это означает, что файлы cookie будут сохраняться между сессиями пользователей. Благодаря этому, если пользователь выйдет из приложения и зайдет, например, на следующий день, он будет автоматически аутентифицирован. (Есть и другие полезные свойства, определенные в классе AuthenticationProperties, но свойство IsPersistent является единственным, который широко используется на данный момент.)

После воссоздания файлов cookie мы перенаправляем пользователя по URL-адресу, который он просматривал до аутентификации (используя параметр returnUrl).

Давайте протестируем процесс аутентификации. Запустите приложение и перейдите по адресу /Home/Index. Браузер перенаправит вас на страницу входа, где необходимо ввести данные пользователя, которого мы создали ранее при тестировании панели администратора:

Ссылка на основную публикацию
Статьи c упоминанием слов:
Adblock
detector