1. Как делать не надо 💅
Матрица доступа — это не «таблица ролей ради таблицы». Это компактная форма проектного решения. Она помогает ещё до конфигурации увидеть, какие зоны реально живут внутри продукта и какие действия требуют не просто аутентификации, а ownership или привилегий.
Особенно полезно то, что матрица заставляет обсуждать действия, а не только пути. Один и тот же ресурс может иметь разные правила на чтение, обновление, публикацию и удаление. Если это не вынесено в явный вид, мозг начинает лениться и всё сводит к одному грубому ярлыку «ну закрытый endpoint».
В проекте Secure Content Platform API матрица хорошо собирается уже на первом уровне, потому что сам домен достаточно компактный. И именно в этом его сила: на маленьком проекте видно почти весь курс.
2. Базовая access matrix Secure Content Platform API
Ниже — стартовая матрица проекта. Это ещё не готовая SecurityFilterChain, не аннотации и не код enforcement. Это карта доступа в бизнес-словах.
| Endpoint | Действие | Базовая зона | Дополнительное условие |
|---|---|---|---|
| GET /api/public/articles | читать опубликованные статьи | public | нет |
| GET /api/public/articles/{slug} | читать одну опубликованную статью | public | нет |
| GET /api/me | получить текущего пользователя | authenticated | caller должен быть известен системе |
| GET /api/me/profile | читать свой профиль | authenticated | маршрут уже self-scoped |
| PATCH /api/me/profile | обновлять свой профиль | authenticated | маршрут уже self-scoped |
| GET /api/drafts | читать список своих черновиков | authenticated | список строится от текущего caller’а |
| GET /api/drafts/{id} | читать конкретный черновик | owner-only | объект должен принадлежать caller’у |
| PATCH /api/drafts/{id} | редактировать черновик | owner-only | объект должен принадлежать caller’у |
| POST /api/drafts/{id}/submit | отправить черновик на модерацию | owner-only | отправлять можно только свой draft |
| GET /api/editor/review-queue | видеть очередь на модерацию | editor | нужен EDITOR или ADMIN |
| POST /api/editor/drafts/{id}/publish | публиковать материал | editor | нужен EDITOR или ADMIN |
| PATCH /api/admin/users/{id}/roles | менять роли пользователя | admin | нужен ADMIN |
| PATCH /api/admin/users/{id}/lock | блокировать или разблокировать пользователя | admin | нужен ADMIN |
| POST /api/auth/register | регистрировать нового пользователя | public | нет |
| POST /api/auth/login | входить в систему | public | нет |
У такой таблицы есть одно сильное свойство: она убирает туман. Сразу видно, что /api/me/profile и /api/drafts/{id} похожи только внешне. В первом случае self-scope уже встроен в дизайн маршрута. Во втором route сам по себе не знает, чей именно объект выбрали по id, и ownership становится обязательной частью правила.
Сразу видно и другое: формула «почти всё authenticated» здесь уже не работает как честное описание системы. В домене есть публичная витрина, личная зона, объектные owner-only случаи, редакторские операции и административные действия. Это уже полноценная модель доступа, а не косметическая надстройка.
3. Одной колонки role недостаточно
Новички очень любят превращать матрицу доступа в «таблицу ролей». Это понятный инстинкт: роли выглядят простыми и удобными. Но как только в системе появляется правило «свой объект / чужой объект», одной роли уже мало.
Представьте двух пользователей с ролью USER. Оба аутентифицированы, оба честные, оба входят в систему корректно. Но это не значит, что каждый из них должен читать и редактировать любой draft по произвольному id. Здесь правило зависит не только от роли, но и от отношения между caller’ом и объектом.
Поэтому хорошая матрица почти всегда содержит не только колонку «кто», но и колонку «какое дополнительное условие». Где-то это условие пустое. Где-то оно звучит как «self-scoped route». Где-то — как «объект должен принадлежать caller’у». Именно в этом месте матрица перестаёт быть грубой «таблицей ролей» и становится картой реальных security-решений 📌
Есть и ещё одна тонкость. У одного и того же пути разные HTTP-методы могут означать разные действия. GET /api/public/articles/{slug} и DELETE /api/public/articles/{slug} — это не «один и тот же ресурс, значит примерно одинаково». Метод здесь меняет бизнес-смысл, а вместе с ним почти наверняка меняется и правило доступа.
4. Шаблон быстрого аудита доступа для любого API
Ниже — тот самый артефакт, который полезно унести с собой уже сегодня. Это не привязка только к нашему проекту. Это короткий шаблон аудита, который можно применить к любому API, где вы работаете или учитесь.
| Шаг | Вопрос | Что вы должны зафиксировать |
|---|---|---|
| 1 | Какое это действие в бизнес-словах? | не «endpoint существует», а «что реально делает система» |
| 2 | Можно ли вызвать это действие анонимно? | public или не public |
| 3 | Если анонимно нельзя, какого caller’а ждёт система? | как минимум authenticated, а иногда конкретная роль |
| 4 | Есть ли связь caller’а с конкретным объектом? | ownership, self-scope, «свой / чужой» |
| 5 | Это влияет на чужие данные, модерацию или управление системой? | editor / admin-уровень или похожая привилегия |
Это очень простая таблица, но в ней как раз и есть практическая сила. Она не требует от вас прямо сейчас знать все детали Spring Security. Она требует только честно проговорить логику доступа. А именно этого чаще всего и не хватает на раннем этапе проектирования.
Если использовать этот шаблон регулярно, он быстро начинает ловить те места, которые команда обычно пропускает: ownership на уровне объекта, слишком грубое authenticated там, где нужно owner-only, и административные операции, замаскированные под «обычный CRUD».
5. На одной матрице уже виден весь курс ✨
И вот здесь случается приятный момент: на одной стартовой матрице уже видно, зачем весь курс вообще существует и как он собран по модулям.
| Что видно в матрице уже сейчас | Что курс добавит дальше |
|---|---|
| есть публичные и непубличные зоны | starter-security, secure-by-default и первые явные правила на уровне запроса |
| системе нужен известный caller | users, passwords, session-based security и классическая stateful-модель |
| есть owner/editor/admin-различия | request-level authorization, method security и бизнес-правила доступа |
| caller должен стать реальным, а не учебным | DB-backed users и нормальная модель пользователя |
| часть клиентов захочет stateless-путь | JWT fundamentals и переход к stateless security |
| доступ нужно не только настроить, но и удерживать | security tests, hardening и troubleshooting |
Это очень хороший эффект для первого уровня. Вы ещё не писали большую конфигурацию, не трогали filter chain и не строили JWT. Но уже видно, что курс не распадается на случайные темы. Он последовательно разворачивает одну и ту же картину доступа в работающий backend.
Именно поэтому Secure Content Platform API — хороший учебный домен. Он маленький, но не игрушечный. На нём видно почти всё, что потом реально встретится в обычном приложении: публичное чтение, текущий пользователь, owner-only объекты, редакторская зона, админские действия, потом users, session, DB, JWT и тесты. Это уже не просто вводный разговор. Это карта всей дальнейшей дороги.
6. Что курс сознательно не пытается покрыть
Доверие к курсу сильно растёт, когда у него чётко названы границы. Поэтому важно сразу сказать: этот курс не пытается в первый же заход закрыть всю мировую security-повестку.
Здесь не будет полноценного OAuth2 / OIDC-трека, внешних identity-провайдеров, SSO, gateway security, распределённой передачи токенов и микросервисной платформенной экзотики. Не потому, что эти темы не нужны. А потому, что сначала надо собрать устойчивую картину внутри обычного Spring Boot backend’а: понять request lifecycle, users, passwords, request rules, owner checks, stateful и stateless модели.
Именно поэтому курс так хорошо стоит в линейке. После Spring Boot и REST API Design он добавляет недостающий слой доступа. А после него уже логично идти в Spring Security Advanced: OAuth2 / OIDC, если продуктовая задача действительно требует внешней identity-платформы. Такая граница делает курс не слабее, а честнее.
7. Типичные ошибки 🚧
Ошибка №1: почти всё помечать как authenticated.
Это очень частая «удобная» деградация матрицы. Кажется, будто вы уже закрыли API от анонима и можно жить дальше. Но вместе с этим теряются ownership-случаи, editor/admin-границы и реальные различия между действиями.
Ошибка №2: хранить матрицу только в голове команды.
Первые пару дней это ещё как-то работает. Потом появляются новые маршруты, новые люди, новые предположения, и проект начинает сам себе противоречить. Явная матрица нужна именно затем, чтобы политика доступа была видимой, а не устной.
Ошибка №3: пытаться выразить ownership только через роли.
Роль USER не знает, чей это объект. Если матрица не умеет зафиксировать дополнительное условие «caller — владелец», значит она пока слишком груба для реального backend’а.
Ошибка №4: думать только путями и забывать про действия и HTTP-методы.
У одного ресурса могут быть совершенно разные правила на GET, PATCH, POST и DELETE. Матрица, которая видит только путь и не видит действие, рано или поздно начнёт скрывать реальные различия доступа.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ