Как разработать систему управления правами доступа для веб-сайта?

Прошу прощения, если я выбрал неверный тег для этого вопроса. Был бы признателен за советы по корректировке. 

Вот суть моего вопроса: 

Я работаю над проектом на основе <b>FastAPI</b> и <b>React.js</b> и столкнулся с трудностями на этапе авторизации и проверки прав пользователя. Авторизация осуществляется через <b>Discord Oauth2</b>, и после успешной авторизации на фронтэнд возвращается <b>jwt токен</b>, который используется для взаимодействия. Однако, мне хотелось бы реализовать систему, сходную с той, что существует в <b>Discord</b>, то есть осуществлять динамическую проверку прав доступа к функционалу с помощью числового кода. Например, если я отключу возможность оставлять комментарии для конкретного пользователя в админке, то это должно сразу отобразиться на фронте. 

Я рассмотрел несколько возможных решений: 

1. Создать JWT токен с встроенным числовым кодом прав, но это решит проблему динамического обновления прав.
2. При каждом запросе обращаться к базе данных для проверки прав, сохраняя в объекте пользователя словарь <b>permission</b> с ключом и значением, например, <b>{ADD_SERVER: 0 или False}</b>. Однако, у меня есть опасения по поводу ресурсозатратности этого подхода и его целесообразности.

Буду благодарен за идеи и рекомендации от тех, кто имеет опыт в данной области. У меня нет коммерческого опыта, и мне интересно узнать, как такие системы реализуются в компаниях. Заранее спасибо!
  • 26 сентября 2024 г. 19:13
Ответы на вопрос 3
Разработка системы управления правами доступа (RBAC - Role-Based Access Control) для веб-сайта может быть сложной задачей, особенно когда речь идет о динамических правах пользователя. Ваш проект на FastAPI и React.js в сочетании с OAuth2 для авторизации предоставляет хорошие возможности для реализации таких решений. Давайте обсудим несколько подходов и их плюсы и минусы.

### 1. JWT с правами доступа

Использование JWT с встроенными правами доступа для пользователя удобно, обеспечивая авторизацию «без состояния» (stateless). Однако, как вы правильно заметили, это затрудняет динамическое изменение прав.

**Преимущества:**
- Быстродействие. Токены могут хранить права доступа, которые проверяются на клиенте или сервере без обращений к базе данных.
- Удобство, т.к. права доступа инкапсулированы в токен.

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

### 2. Запросы к базе данных для каждой операции

Этот подход требует проверки прав доступа на сервере перед выполнением действий пользователя. Это можно реализовать через middleware в FastAPI.

**Преимущества:**
- Динамичное управление правами: если администратор изменит права пользователя, это немедленно отразится на интерфейсе без необходимости обновления токена.
- Простота поддержки и контроля прав доступа.

**Недостатки:**
- Может привести к увеличенному количеству запросов и снижению производительности (особенно при частых проверках).
- Нагрузка на базу данных.

### 3. Кеширование прав доступа

Чтобы снизить нагрузку на базу данных, можно использовать кеширование. Например, при первом запросе пользователя запрашивать его права из БД и кэшировать результат на некоторое время (например, 5-10 минут).

**Преимущества:**
- Устранение избыточных запросов к базе данных.
- Быстродействие при последующих запросах.
  
**Недостатки:**
- Необходимость обеспечить механизм инвалидации кеша при изменении прав.

### 4. Комбинированный подход

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

**Пример реализации:**
1. При логине пользователь получает JWT с базовой информацией (ID, роли и базовые права).
2. Для сложных проверок (например, действий, изменяемых в админке) выполняется запрос к базе данных.
3. Реализуйте определенный кэш для прав доступа и настройте его инвалидацию при изменении.

### Рекомендации

1. **Логику проверки прав добавьте в middleware**. Это позволит вам разделить бизнес-логику и логику авторизации.
2. **Передавайте права доступа в фронтенд только если это необходимо**, чтобы уменьшить объем данных, передаваемых клиенту.
3. **Документируйте все возможные права доступа** и их управления, чтобы сделать его простым для исходного кода и командной работы.

Такой подход позволит вам иметь как удобство статической авторизации, так и динамическое управление правами. В конечном счете выбор зависит от ваших требований к производительности, удобству и спецификации проекта.
Никогда не писал на FastAPI, но наверняка в нём подходы не сильно отличаются от любых других фреймворков. Можно добавить в токен список ролей пользователя на момент выдачи токена, а проверку ролей сделать декоратором на обработчиках запросов, типа 

@app.get("/some/endpoint")
@has_role("some-role")
def some_handler(...):
    ...

Если хочется динамически менять роли на эндпоинте, то можно проверку делать в какой-нибудь там middleware и походы в БД за списком ролей и соответствием ролей эндпоинтам просто кэшировать. Обычно эндпоинтов и ролей не сотни, так что даже их комбинации много памяти не сожрут.
У меня был похожий по функционалу кейс, решил его комбинацией Того, что Вы рассматриваете. 
в JWT маска прав, в redis оверрайд этих прав с TTL чуть больше чем TTL JWT.
Клиет получает JWT с маской прав, по окончании срока жизни JWT он обновляется с новой маской, а если в промежутке возникли изменения они вносятся в редис со сроком жизни чуть больше чем JWT.
Похожие вопросы