Админ-панель для сайта
03-05-2024Время чтения ~ 12 мин.Albireo Framework / CMS 1039
Важная тема, которая затрагивает практически любого владельца сайта. Есть мнение, что сайт с админ-панелью — это что-то очень сложное и даже многие разработчики думают о разработке админ-панели с благоговейным трепетом. Когда-то я сам попался на эту удочку... При разработке MaxSite CMS я выделил себе для админ-панели где-то неделю неспешного времени. Так мне казалось, что задача трудная. На деле всё было сделано за пару часов.
Я попал в ловушку WordPress. Зная эту систему, и зная, насколько там всё запутано (хотя речь идёт о ещё той старой версии 2.0-2.3, сейчас там вообще «тёмный лес»), я предполагал, что буду долго делать нечто похожее. Но в процессе работы я понял, что всё намного-намного проще. По сути админ-панель ничем не отличается от любой другой страницы сайта.
Даже шаблон, который используют для админ-панели принципиально не отличается от любого другого шаблона. Разница разве что в том, что шаблон сайта выводит страницы пользователя, а админ-панель формирует страницы автоматом для управления.
Так что же такое админ-панель по настоящему? На самом деле — это всего лишь механизм управления доступом пользователя к заданным страницам сайта.
То есть если сайт имеет систему разграничения доступом пользователей, то он уже может иметь админ-панель.
Если рассматривать общий алгоритм работы, то будет примерно таким:
- При входе в админ-панель (или любую «закрытую» страницу сайта), проверяется разрешение для доступа.
- Если разрешение есть, то страница работает без ограничений.
- Если доступ запрещён, то выдаём сообщение и прекращаем работу страницы.
Вначале покажу, как это работает на Albireo CMS, а потом детально опишу некоторые тонкости.
Система пользователей
Все пользователи хранятся в отдельном конфиг-файле. Есть логин, ник, хэш пароля и есть массив уровней доступа, например ['admin', 'reader', 'subscriber']
. Вот эти все разрешения (level) совершенно произвольны. На уровне ядра системы есть есть только один «admin», которому можно всё. Остальное на усмотрения владельца сайта.
Причём что важно, если в других системах нужно где-то эти разрешения или уровни доступа регистрировать, то здесь нет — то есть вообще пофиг кто что придумает. По сути это произвольные данные.
Когда нужно ограничить вывод страницы для определённого уровня, то в её поле указывается уровень доступа в «user-level», что-то вроде такого:
/** title: Albireo CMS user-level: admin **/
При необходимости можно указать сразу несколько уровней доступа через запятую.
Система, при выводе этой страницы, автоматом проверит текущего пользователя и его разрешения для этой страницы и сохранит некую «отметку» userAccess
. И на этом больше ничего делать не будет.
Но, зато теперь эта userAccess
будет доступна в коде самой страницы, но проще сделать что-то вроде такого:
if (noUserAccess(message: '<h2>Access denied</h2>')) return;
Таким образом любой пользователь, у которого нет разрешения, будет заблокирован.
Эта же самая схема будет работать для задачи, когда нужно ограничить только часть контента.
открытый текст <?php if (noUserAccess(snippet: 'access4')) goto end; ?> закрытый текст <?php end: ?>
То есть понятно, что вариации самые, что ни на есть разные. Можно скрывать любую часть текста, хоть через предложение. Принципиальной разницы, что именно будет делать эта страница, нет.
Что касается дизайна то, опять же, нет никакой разницы по сравнению с обычной страницей сайта. Можно использовать любую модульную сетку или отдельный шаблон.
Меню админки
Возникает вопрос как именно организовать пункты меню админки. Проще всего придумать отдельную функцию, которая будет сама смотреть конфиг-файл для админки. Если это массив, то можно задавать любые параметры, от css-классов, иконок, вложенности и т.д.
Если в админке предусматривается ещё какой-то динамический блок, например система уведомлений, то делается соответствующая функция, которая и обслуживает этот блок.
С большинстве современных CMS, админка снабжается достаточно сложным механизмом хуков, чтобы прицепиться к адресу, php-файлу, классу, функции и т.п. В итоге всё это требует обслуживания и запутанных функций, смысл которых в итоге сводится к формированию массива, который и обходится в цикле для вывода.
Кто-то думает, что админ-панель это такая святая корова, к которой пользователю нефиг касаться. Это ошибка, потому что пользователь, как раз и заинтересован в том, чтобы свободно добавлять произвольные админ-страницы. Именно поэтому админ-панель и управляется через конфиг-файл, а значит все хитросплетения для вывода меню просто не нужны. Сразу работаем с итоговым php-массивом.
Формы, AJAX, POST и т.п.
Формы верстаются точно также как и для сайта. Отправка post-запроса работает точно так же как и для обычной страницы — указывается url-адрес обработчика, а это обычная страница. В ней точно также указываем уровень доступа и проверяем его в php-коде страницы. Это на тот случай, если кто-то отправит запрос вне логина.
Что касается Ajax, то можно сделать точно также как и для Post, но можно использовать предопределённый механизм, который включает в себя некий файл-обработчик handler
. Это обычный php-файл, только он работает не как страница, а как простой php-файл (но в рамках системы). Разница только в том, что система сама его найдёт и подключит, а после этого рубанёт через exit()
.
И опять же, принципиальной разницы будет ли это ограничение доступа или нет, не имеет значения. Механизм работает одинаково для любых частей сайта.
Итого по Albireo CMS
При желании можно Albireo CMS использовать как админку для произвольного сайта. Поскольку её вообще по барабану какие вы заходите использовать адреса и какой будет функционал, она идеально подходит для самостоятельного приготовления админ-панели.
Конечно, же, придётся придумать чем именно нужно управлять, но это уже чистое программирование, без заморочек на обслуживание доступа и прочими чудесами.
Теперь рассмотрим технические детали более подробно.
Что лучше использовать для авторизации пользователей
По сути есть две схемы хранения данных авторизации — это куки и php-сессии. Куки — более распространённый вариант, более старый и больше отработанный. Смысл его в том, что после проверки формы логина, система сохраняет куку, где хранит данные пользователя. Соответственно, при загрузке системы, проверяется кука и если там есть верные данные, то считается, что пользователь авторизован.
Проблемой кук будет то, что ими сложно управлять и легко допустить алгоритмические ошибки. Например, часто хранят в куке признак авторизации, что-то вроде 1 или 0. Теоретически, если взломать эту куку, можно элементарно выставить её в «1» и система будет считать постороннего пользователя залогиненным.
Поэтому часто в куке нужно хранить пароль (или хэш) и логин, а при старте нужно проверять эту комбинацию на реальных данных. То есть делать запрос к базе данных и смотреть есть ли там вообще такой юзер и проверять его пароль. Очевидно, что на это навешивается большой пласт технических проблем по шифровке и дешифровке.
Кроме того, работа с куками, предполагается как самый первый этап работы с системой — до любого вывода в браузер. И это тоже большая проблема, поскольку порой сложно отследить все чудеса пользовательского кода, а значит начинаются танцы с бубном вокруг буферизации вывода.
Конечно, всё это делается, но подводных камней предостаточно.
Сессии в этом плане выглядят более привлекательней. Сейчас в PHP уже отработаны многие вопросы по сессиям, что делает работу с ними удобной.
При загрузке системы, включается механизм сессий. Для этого PHP смотрит куку, которая хранит код сессии и проверяет есть ли она на сервере. Сама по себе сессия — это простой текстовый серилизованный массив данных в файле. Поэтому работа с этим файлом/сессией ничем не отличается от работы с обычным массивом ($_SESSION
), а PHP берёт на себя всю работу по обслуживанию файла.
Таким образом в сессии можно хранить что угодно, включая логин и пароль пользователя. С точки зрения безопасности, лучше критические данные, тот же пароль, не хранить в открытом виде, но можно хранить его хэш, а потом сверять через password_verify()
.
Плюс сессии ещё в том, что кука — это часть http-заголовка и когда она разрастается, что частенько случается, то вываливается 500-ошибка сервера. Кука сессии — это маленький идентификатор, который вряд ли скажется на переполнение размера http-заголовка.
Ещё о проблемах кук
Если делать на куках, то всё равно куки придётся ставить для всех посетителей. Это нужно например для того, чтобы отслеживать их действия. Скажем дублировать повторную отправку формы, или делать паузу между комментариями. Или голосование/рейтинг... То есть ситуаций много. Именно поэтому большинство CMS автоматом генерируют куки с уникальными идентификаторами даже для неавторизованных посетителей.
В большинстве случаев это техническая проблема, направленная на удобство посетителей. Поэтому разработчики часто «косячат» с куками, потому что нельзя собрать слишком много данных. Даже соглашение об отказе от кук, требует установки куки.
Поэтому, рассматривая вопрос только с технической точки зрения, куки — это существенное ограничение. Обратите внимание, как часто в Интернет-магазинах используются многочисленные get-параметры в url-адресах. Поскольку всё это запихнуть в куку не получится (вывалится 500-ошибка), они вынуждены добавлять эти данные в адреса.
Другой проблемой кук может быть их безопасность. Если данные хранятся в куке, то теоретически всегда есть вероятность их расшифровать. При этом даже не придётся делать запросы к сайту, а пробовать подобрать ключ локально без ограничений. Точно также есть проблема воровства кук с помощью JavaScript. Отдельно стоит упомянуть атаки, когда подменяют или воруют куки у ничего не подозревающих пользователях. Причем некоторые из них достаточно специфичные, например есть атака через svg-файл.
Как хранить данные пользователей?
Стандартно есть логин и пароль. Есть, конечно системы, когда нужно ввести только пароль, но по мне так, это не лучший вариант, поскольку годится только для однопользовательской системы. Если пользователей несколько, то лучше их разделять уникальными логинами (но не никами).
В Albireo CMS пользователи — это обычный php-массив. В других системах это может быть таблица в базе данных. Фактически там будут те же поля: логин, пароль, ник, доступ и т.п.
Из всего этого, в открытом виде хранить нельзя только пароль, поскольку остальные данные нужны в самой системе. Для пароля следует использовать хэш и сейчас по факту это password_hash()
.
В качестве шифрования лучше использовать не только сам пароль, но и логин:
password_hash($password . $login, PASSWORD_DEFAULT);
Если совсем хочется заморочиться, то можно добавить и секретный ключ сайта. Тогда при смене ключа, придётся переделывать все хэши пользователей, что не очень удобно. А так получается связка «логин+пароль», которую подобрать также сложно.
Если система работает с критическими данными, то наверное стоит добавить ещё и обратное шифрование для этих данных. То есть идея в том, что если случится взлом, то время на раскодирование критичных данных будет сильно дольше, чем залатать «дыру».
Система разрешения для пользователей
Обычно это сложный и запутанный алгоритм. Проблема, как мне видится, лежит в исторической плоскости. Упомянем недобрым словом WordPress, который заложил некий «стандарт» в этом вопросе. Речь идёт о разделении пользователей на некие роли. Каждая роль — это группа разрешений, обычно отмечаемых в админ-панели.
Соответственно, каждый модуль, каждый плагин, вообще всё, что хоть немного меняет в системе, получает своё уникальное разрешение. И при настройке «роли» нужно его выбрать.
На практике это всё довольно муторно реализуется, и также настраивается.
Если включить голову, то можно понять, что ограничение пользователя всегда должно работать как доступ к (админ-)странице и похеру, что именно там делается.
Потому что практически всегда модуль/плагин, где нужно разрешение пользователя, и формирует эту самую страницу. По факту все эти роли и их разрешения и срабатывают как ограничение к странице. Проблема в том, что если промахнуться, то какие-то пользователи смогут получить доступ к тем модулям, к которым они не должны прикасаться. То есть элементарная проверка между «==» и «===» может привести к проблемам разрешений. И на этом строится масса уязвимостей того же WordPress.
Если же отказаться от этого механизма, то все ограничения доступа проверятся «в лоб» — если страница имеет уровень «admin», то ни один другой пользователь к ней не получит доступа. И проверка сводится к примитивному нахождению указанного уровня доступа страницы user-level
в массиве levels
текущего пользователя с помощью in_array()
.
Проблема «ролей» ещё в том, что они плохо подходят для специфичных задач. Например, ваш сосед попросил выложить на сайт некий секретный файл. Чтобы его не светить, вы делаете для соседа пару «логин+пароль» и указываете разрешение «something spy». Дальше на странице просто указываете это разрешение user-level: something spy
, под него строчку кода проверки, ну и, собственно, всё.
Если же использовать роли, то придётся выполнить кучу телодвижений, чтобы создать роль, потом поотключать все лишние разрешения, потом прописать в модуле собственное разрешение, причем ещё неизвестно позволит ли система проверку доступа на уровне страниц, или всё-таки требуется вход в админ-панель всего сайта.
При этом, если нужно ограничить только какой-то особый блок кода, то через роли это сделать крайне проблематично, поскольку у пользователя будет только одна роль. Если же использовать обычные разрешения, то их может быть сколько угодно и проверяются они одной строкой кода.
Дизайн админ-панели
Мой совет — ничего особенно не изобретать. Сейчас полно и бесплатных шаблонов, и рабочих примеров из CMS. Нужно понимать, что админ-панель не может быть вещью в себе — она имеет чётко определённый функционал, который завязан на возможности CMS. Поэтому все блоки админки должны иметь строго определённую функциональность. Если нет профиля пользователя или поиска, то такие блоки в админке не нужны. Часто дизайнеры малюют кучу бессмысленных элементов, которые не имеют практического применения. Поэтому лучше начать с чего-то простого.
Цвет фона админки имеет практическое значение. Многим нравится темный фон или просто притенённый, но лучше использовать тот, который используется на сайте. Обычно это чистый белый для текста. Если в админке другой цвет, то редактор будет видеть цветовое искажение.
По факту в админке должны быть только два обязательных блока. Контентная часть, где выводится сама управляющая страница, и блок навигации по админ-страницам.
Как правило блок навигации размещают слева. Такое расположение считается классическим и оно применяется в подавляющем большинстве CMS: MaxSite CMS, WordPress, Albireo Framework (включая doc-шаблон) и т.п. Если админка предполагается большая, то наверное это самый удобный вариант.
Если же админка более простая, то блок навигации лучше разместить вверху в виде обычного меню. Ещё этот способ размещения удобен тем, что на контентную часть отдаётся вся ширина браузера, и это особенно полезно для страницы редактирования записей. Потому что часто нужно разместить дополнительные опции и для этого хорошо подходит именно колонка справа. Поэтому ширина контентной части должна быть как можно больше.
Также это используется для быстрого предпросмотра редактируемой записи. Здесь располагается iframe, который должен занимать всю ширину браузера.
Если пунктов меню много, то есть вариант сделать их выпадающими, либо в виде табов — аля-wp версий 1.5-2.6.
То есть админку нужно сделать такой, чтобы она работала быстро, и при этом была удобной. Для редактирования текста лучше сделать так, чтобы все посторонние элементы вообще скрыть — это такой дзен-режим для писателей и кодеров.
Итого
Админ-панель не такая сложная вещь, если понимать её как часть механизма доступа пользователей. Если решить этот вопрос, то всё остальное вторично.
Слава Украине! Смерть рашистам!