Через какую концепцию реализуется авторизация
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Концепции реализации авторизации
Авторизация — это процесс проверки прав доступа пользователя к ресурсам. Это отличается от аутентификации (проверка личности пользователя). Авторизация реализуется через несколько ключевых концепций.
1. RBAC (Role-Based Access Control) — Контроль доступа на основе ролей
Это самая популярная концепция. Пользователю присваивается роль, а роль определяет, что пользователь может делать.
from enum import Enum
from typing import Set
from dataclasses import dataclass
class Role(Enum):
ADMIN = "admin"
MODERATOR = "moderator"
USER = "user"
GUEST = "guest"
@dataclass
class User:
id: int
email: str
roles: Set[Role]
# Определяем права для каждой роли
ROLE_PERMISSIONS = {
Role.ADMIN: {"create_user", "delete_user", "view_logs", "edit_post"},
Role.MODERATOR: {"edit_post", "delete_comment", "ban_user"},
Role.USER: {"create_post", "create_comment", "edit_own_post"},
Role.GUEST: {"read_post", "read_comment"},
}
def has_permission(user: User, permission: str) -> bool:
"""Проверяет есть ли у пользователя нужное разрешение."""
for role in user.roles:
if permission in ROLE_PERMISSIONS.get(role, set()):
return True
return False
# Пример использования в API
from fastapi import FastAPI, Depends, HTTPException
app = FastAPI()
def require_permission(permission: str):
async def check_permission(user: User = Depends(get_current_user)):
if not has_permission(user, permission):
raise HTTPException(status_code=403, detail="Forbidden")
return user
return check_permission
@app.post("/api/users")
async def create_user(user: User = Depends(require_permission("create_user"))):
# Только пользователи с правом create_user могут создавать юзеров
return {"status": "user created"}
2. PBAC (Permission-Based Access Control) — Контроль доступа на основе разрешений
Более гранулярная концепция. Вместо ролей проверяются конкретные разрешения (permissions).
from dataclasses import dataclass
from typing import Set
@dataclass
class Permission:
name: str
description: str
@dataclass
class User:
id: int
email: str
permissions: Set[Permission]
# Разрешения (более детальные)
PERMISSION_CREATE_POST = Permission("create_post", "Может создавать посты")
PERMISSION_DELETE_POST = Permission("delete_post", "Может удалять посты")
PERMISSION_EDIT_OTHER_POST = Permission("edit_other_post", "Может редактировать чужие посты")
def check_permission(user: User, required_permission: Permission) -> bool:
return required_permission in user.permissions
# Удобнее для гибких систем разрешений
class PermissionBasedAccessControl:
def __init__(self, user: User):
self.user = user
def can(self, permission: Permission) -> bool:
return permission in self.user.permissions
def can_delete_post(self, post_author_id: int) -> bool:
# Может удалять свои посты ИЛИ иметь разрешение на удаление
if post_author_id == self.user.id:
return True
return self.check_permission(self.user, PERMISSION_DELETE_POST)
3. ABAC (Attribute-Based Access Control) — Контроль доступа на основе атрибутов
Самая гибкая концепция. Доступ зависит от атрибутов пользователя, ресурса, окружения.
from dataclasses import dataclass
from typing import Any, Dict
from datetime import datetime
@dataclass
class AccessContext:
user_id: int
user_department: str
user_level: int
resource_type: str
resource_owner_id: int
request_time: datetime
request_ip: str
def check_access_abac(context: AccessContext) -> bool:
"""
Проверяет доступ на основе атрибутов.
Правила очень гибкие.
"""
# Правило 1: Администраторы (level > 100) могут всё
if context.user_level > 100:
return True
# Правило 2: Владелец ресурса может редактировать его
if context.user_id == context.resource_owner_id:
return True
# Правило 3: Менеджеры (level >= 50) могут работать с ресурсами своего отдела
if context.user_level >= 50 and context.resource_type == "department_report":
# Проверяем БД для связи
return True
# Правило 4: Доступ только в рабочие часы (9-17)
if 9 <= context.request_time.hour < 17:
if context.user_level >= 30:
return True
# Правило 5: Доступ из офиса
office_ips = ["192.168.1.0/24", "10.0.0.0/8"]
if context.request_ip in office_ips:
return True
return False
# Использование с FastAPI
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/api/resource/{resource_id}")
async def get_resource(resource_id: int, request: Request):
user = get_current_user() # аутентификация
context = AccessContext(
user_id=user.id,
user_department=user.department,
user_level=user.access_level,
resource_type="document",
resource_owner_id=get_resource_owner(resource_id),
request_time=datetime.now(),
request_ip=request.client.host,
)
if not check_access_abac(context):
raise HTTPException(status_code=403, detail="Forbidden")
return get_resource(resource_id)
4. JWT (JSON Web Tokens) — Токены для авторизации
Механизм передачи прав в API.
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthCredentials
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
# Создание токена
def create_access_token(user_id: int, roles: list, expires_in_hours=24):
payload = {
"user_id": user_id,
"roles": roles,
"exp": datetime.utcnow() + timedelta(hours=expires_in_hours),
"iat": datetime.utcnow(),
}
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
return token
# Проверка токена
def verify_access_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id = payload.get("user_id")
roles = payload.get("roles", [])
return {"user_id": user_id, "roles": roles}
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
# Использование в FastAPI
security = HTTPBearer()
app = FastAPI()
@app.post("/api/login")
async def login(email: str, password: str):
# Проверяем пароль (аутентификация)
user = authenticate_user(email, password)
if not user:
raise HTTPException(status_code=401, detail="Invalid credentials")
# Создаём токен (авторизация)
token = create_access_token(user.id, user.roles)
return {"access_token": token, "token_type": "bearer"}
@app.get("/api/protected")
async def protected_route(credentials: HTTPAuthCredentials = Depends(security)):
payload = verify_access_token(credentials.credentials)
# Теперь знаем user_id и roles
user_id = payload["user_id"]
roles = payload["roles"]
return {"user_id": user_id, "roles": roles}
5. OAuth 2.0 — Стандарт для делегированного доступа
Используется для разрешения доступа к ресурсам от имени пользователя.
from fastapi import FastAPI
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
user_id: int
scopes: list
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Проверяем учётные данные
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=401, detail="Invalid credentials")
# Возвращаем токен доступа
access_token = create_access_token(
data={"user_id": user.id, "scopes": user.scopes}
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
user = verify_token(token)
return {"user_id": user.user_id, "scopes": user.scopes}
6. ACL (Access Control List) — Списки контроля доступа
Для специфичных прав на конкретные объекты.
from typing import Dict, Set
class ACL:
def __init__(self):
# resource_id -> {user_id -> permissions}
self.permissions: Dict[int, Dict[int, Set[str]]] = {}
def grant(self, resource_id: int, user_id: int, permission: str):
if resource_id not in self.permissions:
self.permissions[resource_id] = {}
if user_id not in self.permissions[resource_id]:
self.permissions[resource_id][user_id] = set()
self.permissions[resource_id][user_id].add(permission)
def revoke(self, resource_id: int, user_id: int, permission: str):
if resource_id in self.permissions:
if user_id in self.permissions[resource_id]:
self.permissions[resource_id][user_id].discard(permission)
def check(self, resource_id: int, user_id: int, permission: str) -> bool:
return (
resource_id in self.permissions
and user_id in self.permissions[resource_id]
and permission in self.permissions[resource_id][user_id]
)
# Использование
acl = ACL()
acl.grant(resource_id=1, user_id=123, permission="read")
acl.grant(resource_id=1, user_id=123, permission="edit")
# Проверка
if acl.check(resource_id=1, user_id=123, permission="edit"):
print("Доступ разрешён")
Сравнение концепций
| Концепция | Сложность | Гибкость | Масштабируемость |
|---|---|---|---|
| RBAC | Низкая | Средняя | Хорошо |
| PBAC | Средняя | Высокая | Хорошо |
| ABAC | Высокая | Очень | Отлично |
| ACL | Средняя | Средняя | Хорошо |
Практическая реализация в реальном проекте
# Часто используют комбинацию подходов
from functools import wraps
class AuthorizationService:
def __init__(self):
self.role_permissions = {} # RBAC
self.acl = ACL() # ACL
def check_access(self, user: User, resource_id: int, action: str) -> bool:
# Сначала проверяем RBAC
for role in user.roles:
if self._has_role_permission(role, action):
# Затем проверяем ACL
if self.acl.check(resource_id, user.id, action):
return True
return False
def require_access(action: str):
def decorator(func):
@wraps(func)
async def wrapper(resource_id: int, user: User = Depends(get_current_user)):
auth_service = AuthorizationService()
if not auth_service.check_access(user, resource_id, action):
raise HTTPException(status_code=403, detail="Forbidden")
return await func(resource_id, user)
return wrapper
return decorator
Заключение
Авторизация реализуется через разные концепции:
- RBAC — когда роли = разрешения
- PBAC — когда нужна гибкость
- ABAC — для сложных систем
- ACL — для специфичных прав на объекты
- JWT/OAuth — для безопасной передачи прав
Выбор зависит от требований вашей системы.