Типичная история: компании нужен личный кабинет для клиентов, где можно зайти, скачать счёт, посмотреть статус заявки и написать менеджеру. Разработчик ставит плагин членства, к нему файловый менеджер, конструктор форм и ещё одно дополнение для редактирования профиля на фронтенде. Через год на сайте четыре плагина от разных авторов, два из них конфликтуют после обновления, страница кабинета грузится заметно дольше остальных, а клиенты открывают в ней от силы пару разделов.
Личный кабинет на WordPress можно собрать почти целиком на штатных функциях ядра. Плагины при этом нужны, просто не для всего подряд. Дальше по порядку: из каких частей состоит такой кабинет, где без готового решения действительно не обойтись и какие ошибки чаще всего открывают чужие документы посторонним.
Что клиенту на самом деле нужно в кабинете
Прежде чем выбирать способ реализации, выпишите функции кабинета и напротив каждой поставьте, как часто клиент будет ей пользоваться. В B2B-проектах список обычно короткий:
- вход по логину или email и восстановление пароля;
- карточка компании: реквизиты, контактное лицо, телефон;
- документы: счета, акты, договоры, акты сверки;
- статус текущих заявок, заказов или проектов;
- форма обращения к менеджеру с историей переписки.
Рейтинги, бейджи, личные сообщения между пользователями и уровни подписки в кабинете клиента сервисной или торговой компании встречаются редко. Но популярные плагины членства и сообществ написаны как раз под такие сценарии, отсюда их вес и количество настроек.
Полезный вопрос для заказчика: что клиент откроет хотя бы раз в месяц? Если ответ укладывается в три-четыре пункта, кабинет лучше писать под задачу. Если в списке платный доступ к контенту по тарифам, продление подписки и автоматические списания, готовый плагин сэкономит месяцы работы.
Когда плагин оправдан, а когда только добавляет работы
Отказываться от плагинов из принципа нет смысла. Готовое решение выгоднее собственной разработки в трёх ситуациях.
Первая: на сайте уже работает WooCommerce. У него есть своя страница «Мой аккаунт» с разделами заказов, загрузок, адресов и данных учётной записи. Адреса этих разделов (в документации WooCommerce они называются endpoints) настраиваются в меню WooCommerce → Настройки → Расширенные, там же лишний раздел можно отключить, очистив поле. Если магазин уже стоит, кабинет разумно расширять внутри этой страницы. Второй, параллельный кабинет запутает и клиентов, и разработчиков.
Вторая: нужен платный доступ к материалам. Курсы, закрытые базы знаний, подписка с разными уровнями доступа. Вокруг оплаты и сроков там много логики, и повторять её с нуля дорого.
Третья: нужна двухфакторная аутентификация. В ядре WordPress её нет, и писать свою реализацию ради кабинета не стоит. Проверенный плагин для 2FA как раз тот случай, когда дополнительная зависимость себя окупает.
В остальных случаях каждый плагин добавляет постоянные расходы. Его надо обновлять, проверять на совместимость после выхода новой версии WordPress, он расширяет поверхность для атак и часто подключает свои скрипты и стили на страницах, где они не нужны. Кабинет из небольшого собственного плагина на несколько сотен строк кода проще сопровождать: в нём нет ничего, чем компания не пользуется.
Из чего собирается кабинет штатными средствами WordPress
Почти любой кабинет держится на одном и том же наборе функций и хуков ядра. Названия приведены для разработчика. Руководителю достаточно понимать, что всё это часть самого WordPress, и сторонний код сюда не входит.
Отдельная роль для клиентов
Роль создаётся функцией add_role(): ей передают идентификатор, отображаемое название и набор прав. Функция записывает роль в базу данных, поэтому по документации её вызывают один раз, при активации плагина или темы, а не на каждой загрузке страницы. Если роль уже существует, функция вернёт null и ничего не изменит.
Клиентов можно было бы оставить в стандартной роли «Подписчик». Отдельная роль удобнее по двум причинам. По ней легко отличить клиента от сотрудника или случайно зарегистрированного пользователя. И ей можно выдать только нужные права, а потом проверять их через current_user_can(), не завязываясь в коде на название роли.
Вход и регистрация
Форму входа на обычной странице сайта выводит функция wp_login_form(). Она формирует поля логина и пароля, флажок «Запомнить меня» и кнопку. Параметр redirect задаёт, куда отправить пользователя после входа, а параметром remember флажок можно убрать.
Чтобы клиент после входа попадал сразу в кабинет, а администратор в консоль, используют фильтр login_redirect. В него передаётся объект пользователя, и по его роли выбирается адрес. Документация отдельно предупреждает, что глобальная переменная текущего пользователя в этот момент может быть ещё не заполнена, поэтому опираться нужно на переданный объект.
Открытую регистрацию (флажок «Любой может зарегистрироваться» в разделе Настройки → Общие) для B2B-кабинета лучше не включать. Учётную запись клиенту создаёт менеджер после подписания договора, а клиенту уходит письмо со ссылкой для установки пароля. Так в базе не появляются сотни ботов, и кабинет видят только те, с кем у компании есть отношения.
Закрытая страница и разделы кабинета
Сам кабинет удобно сделать обычной страницей, например /kabinet/. Проверку доступа вешают на хук template_redirect: если пользователь не вошёл, вызывается auth_redirect(). Эта функция отправляет посетителя на страницу входа и после авторизации возвращает его на тот адрес, который он запрашивал.
Разделы вида /kabinet/documents/ или /kabinet/zayavki/ создаются через add_rewrite_endpoint() с параметром EP_PAGES. Название раздела становится переменной запроса, и шаблон страницы по ней решает, что показать. Документация требует сбрасывать правила перезаписи функцией flush_rewrite_rules() при активации и деактивации плагина. Если об этом забыть, новые адреса отдадут ошибку 404, и разработчик потратит час на поиски причины.
Данные клиента
Реквизиты и контакты хранятся в метаданных пользователя: WordPress позволяет привязать к учётной записи любые поля. Документы и заявки удобнее оформить отдельными типами записей. У каждой записи есть поле с идентификатором клиента, и кабинет выводит только те записи, где этот идентификатор совпадает с текущим пользователем. Менеджер работает с документами в привычной админке: создаёт запись, прикрепляет файл, выбирает клиента из списка.
Отдельно продумайте уведомления. Письма со ссылкой на установку пароля, о новом документе или смене статуса заявки WordPress отправляет функцией wp_mail(), и по умолчанию она работает через почтовую функцию PHP на сервере хостинга. На многих хостингах такие письма уходят без подписи домена и оседают в спаме. Поэтому до запуска кабинета отправку лучше перевести на SMTP корпоративной почты и проверить, что письмо доходит до Gmail, Mail.ru и почты самого клиента.
Скрытая админка
Клиенту не нужна чёрная панель инструментов сверху и доступ к консоли WordPress. Панель на сайте отключается фильтром show_admin_bar. По документации возврат false и есть рекомендуемый способ её скрыть, причём сделать это можно выборочно, только для роли клиента. Попытку открыть /wp-admin/ перенаправляют обратно в кабинет.
Здесь есть грабля. Через admin-ajax.php и admin-post.php, которые лежат в той же папке, идут AJAX-запросы и отправка форм. Если перенаправление не делает для них исключения, формы в кабинете перестают работать, а в логах ошибок пусто.
Формы внутри кабинета
Обращение к менеджеру или изменение реквизитов отправляют на admin-post.php с параметром action. Для вошедших пользователей срабатывает хук admin_post_{action}, для гостей admin_post_nopriv_{action}, и в кабинете второй обычно не нужен вовсе. Каждая форма содержит одноразовый токен (nonce), а обработчик проверяет токен и права пользователя до того, как что-то запишет в базу. Эти две проверки отсекают подделку запросов со сторонних сайтов и попытки отправить форму от чужого имени.
Документы: самое уязвимое место кабинета
Пароль клиента может быть сколько угодно сложным, и документы всё равно окажутся у посторонних, если лежат в публичной папке. Файлы, загруженные через медиатеку, попадают в /wp-content/uploads/, и веб-сервер отдаёт их напрямую, не спрашивая WordPress, кто их запрашивает. Страница кабинета закрыта, но если ссылка на PDF с актом сверки попала в пересланное письмо или в историю браузера на общем компьютере, файл откроет любой.
Надёжная схема выглядит так:
- Файлы клиентов хранятся вне публичной папки или в каталоге, прямой доступ к которому запрещён настройкой веб-сервера. Как именно это сделать, зависит от хостинга: у Apache и nginx правила разные, и проверять нужно на боевом сервере.
- Клиент скачивает документ по ссылке вида
/kabinet/download/123/. Обработчик проверяет, что пользователь вошёл и что документ 123 принадлежит именно ему, и только после этого отдаёт файл. - Случайное имя файла защитой не считается. Оно усложняет подбор, но от пересланной ссылки не спасает.
- Каждое скачивание пишется в журнал: кто, когда, какой документ. Когда клиент говорит, что акт не получал, такой журнал закрывает вопрос за минуту.
Владельца проверяют на каждом запросе, причём и при выдаче файла, и при открытии страницы документа. Классическая ошибка выглядит так: кабинет показывает клиенту его собственный список, но если в адресе поменять номер документа на соседний, открывается чужой. На тестировании такое не замечают, потому что тестировщик заходит под одной учётной записью.
Один клиент, несколько сотрудников
В B2B клиент обычно компания, а не человек. Счета смотрит бухгалтер, заявки ведёт снабженец, договоры подписывает директор. Если выдать компании один общий логин, пароль через полгода знают все, включая уволившихся, и в журнале скачиваний непонятно, кто что открывал.
Правильнее хранить компанию отдельной сущностью, а каждому сотруднику заводить свою учётную запись с привязкой к этой компании. Документы и заявки тогда принадлежат компании, и проверка доступа сравнивает компанию документа с компанией текущего пользователя. Внутри компании можно разграничить видимость: бухгалтеру показывать счета и акты, снабженцу заявки, контактному лицу всё сразу.
Когда сотрудник уходит, менеджер снимает с его учётной записи роль клиента. Доступ пропадает сразу, а документы компании и история заявок остаются на месте, потому что привязаны к компании, а не к человеку. Этот сценарий стоит заложить в задание на разработку с самого начала: переделывать кабинет с логикой «один клиент, один логин», когда в нём уже лежат сотни документов, заметно дороже.
Пример: кабинет для клиентов сервисной компании
Условный пример, собранный из типовых проектов. Компания обслуживает оборудование примерно у полутора сотен корпоративных клиентов. Бухгалтерия ежемесячно рассылает акты и счета по почте, клиенты теряют письма и звонят с просьбой прислать повторно, а менеджеры по несколько раз в день отвечают на вопрос, на каком этапе заявка.
Первое предложение подрядчика: плагин членства, плагин файлового менеджера, конструктор форм и дополнение для профиля. Четыре лицензии с ежегодным продлением и четыре набора настроек, в которых через год никто не разберётся.
Вместо этого собрали один небольшой плагин:
- роль «Клиент» с единственным правом на просмотр;
- страница кабинета и три раздела: документы, заявки, профиль;
- тип записи «Документ» с полем выбора клиента и защищённой выдачей файла;
- тип записи «Заявка» со статусом, который менеджер меняет в админке;
- форма обращения, которая создаёт новую заявку и отправляет уведомление ответственному.
Бухгалтер загружает акт и выбирает клиента. Документ сразу появляется в кабинете, а клиенту уходит короткое письмо без вложения, только со ссылкой. Просьб прислать документ повторно становится заметно меньше: всё лежит в одном месте. Страница кабинета остаётся лёгкой, на ней нет скриптов, которые плагины подключают на всякий случай. Если через два года сменить тему сайта, кабинет продолжит работать, потому что вся логика в плагине, а не в файле functions.php темы.
Такие задачи мы решаем в рамках проектов по разработке сайтов под ключ. Сначала составляем список функций и сценариев клиента, потом выбираем между готовым плагином и собственным кодом, и только после этого пишем код.
Кабинет и CRM: не дублировать данные
Если у компании есть CRM, кабинет не должен становиться второй базой клиентов. Статусы сделок, заявок и счетов живут в CRM, кабинет их только показывает. Иначе через полгода данные расходятся: менеджер закрыл сделку в CRM, а у клиента в кабинете она всё ещё «в работе».
Схем две. В первой сайт при открытии кабинета запрашивает актуальные данные у CRM через её API. Во второй CRM сама отправляет изменения на сайт, когда меняется статус. Первая проще, но зависит от скорости ответа CRM. Вторая быстрее для клиента, зато требует обработчика на стороне сайта и контроля, что ни одно событие не потерялось.
Для входящих запросов от внешних систем в WordPress с версии 5.6 есть пароли приложений. Они работают с REST API и XML-RPC, а для входа через обычную форму не подходят. Если интеграций нет, эту возможность разумно отключить. Подробнее о связке сайта и CRM мы писали в статье про интеграцию WordPress с CRM без потери заявок.
Безопасность и сопровождение после запуска
Кабинет с документами клиентов повышает цену взлома сайта. Раньше злоумышленник получал доступ к витрине, теперь к договорам и реквизитам. Минимальный набор мер:
- двухфакторная аутентификация хотя бы для администраторов и менеджеров;
- ограничение числа попыток входа;
- HTTPS на всём сайте, без исключений для отдельных страниц;
- резервные копии, в которые попадают и база, и закрытая папка с документами;
- проверка кабинета после каждого обновления WordPress: вход, скачивание документа, отправка формы.
Последний пункт звучит банально, а пропускают его часто. Обновление ядра прошло без ошибок, главная страница открывается, а форма обращения в кабинете уже неделю отдаёт белый экран. Клиенты об этом не пишут. Они звонят менеджеру и решают, что кабинет не работает. Общие меры защиты сайта мы собрали в материале о безопасности WordPress.
Чек-лист перед запуском кабинета
- Вход под учётной записью клиента открывает кабинет, а не консоль WordPress.
- Панель инструментов наверху у клиента скрыта.
- Прямой адрес файла из кабинета без входа не открывается.
- Подмена номера документа в адресе не показывает чужой документ. Проверено под двумя разными клиентами.
- Формы кабинета отправляются, обработчик проверяет токен и права.
- Восстановление пароля работает, письмо доходит и не попадает в спам.
- Разделы кабинета не отдают 404 после деактивации и повторной активации плагина.
- Страницы кабинета закрыты от индексации.
- Резервная копия включает закрытую папку с документами.
- Сотрудник, у которого сняли роль клиента, больше не видит документы компании.
- Менеджер может без разработчика создать клиента, загрузить документ и сменить статус заявки.
Если хотя бы один пункт не выполняется, показывать кабинет клиентам рано. Пункты 3 и 4 стоит перепроверять после любой доработки, даже если она касалась совсем другой части сайта.
Частые вопросы
Можно ли сделать личный кабинет на WordPress без WooCommerce?
Да. Роли, вход, закрытые страницы, разделы кабинета и хранение данных клиента реализуются функциями ядра WordPress. WooCommerce имеет смысл, только если на сайте уже есть магазин: тогда кабинет лучше расширять внутри его страницы «Мой аккаунт».
Безопасно ли хранить договоры и акты в кабинете на WordPress?
Безопасно, если файлы не лежат в публичной папке загрузок и каждый запрос на скачивание проверяет, что документ принадлежит текущему пользователю. К этому стоит добавить двухфакторную аутентификацию для сотрудников, резервные копии и журнал скачиваний.
Что будет с кабинетом при смене темы сайта?
Если логика кабинета вынесена в отдельный плагин, он продолжит работать, поменяется только оформление. Если код написан в файле functions.php темы, при смене темы кабинет пропадёт целиком.
Нужна ли открытая регистрация в кабинете?
Для B2B обычно нет. Учётные записи создаёт менеджер после заключения договора, а клиент получает письмо со ссылкой для установки пароля. Так в базе не появляются боты и случайные пользователи.
Когда всё-таки лучше взять готовый плагин?
Если нужен платный доступ к материалам с уровнями подписки, если на сайте уже работает WooCommerce или требуется двухфакторная аутентификация. Для кабинета с документами, заявками и профилем готовый плагин чаще добавляет работы по сопровождению, чем экономит на разработке.
