← Назад к вопросам
В чем разница между ошибкой авторизации и недостаточных прав?
1.0 Junior🔥 191 комментариев
#REST API и HTTP#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Ошибка авторизации vs Недостаточные права
Авторизация (Authentication) vs Аутентификация
Сначала важно различать два понятия:
- Аутентификация (Authentication) — это процесс идентификации пользователя (кто вы?)
- Авторизация (Authorization) — это процесс проверки прав доступа (что вы можете делать?)
1. Ошибка авторизации (Authentication failure) — 401 Unauthorized
Ошибка авторизации возникает, когда система не может идентифицировать пользователя или пользователь не был проаутентифицирован.
Это означает: система не знает, кто вы.
Примеры ошибок авторизации:
- Вы не передали токен доступа
- Токен истёк или недействителен
- Неверный логин/пароль
- Сессия завершена
- Подпись токена невалидна
from fastapi import FastAPI, HTTPException, status, Depends
from fastapi.security import HTTPBearer
from datetime import datetime, timedelta
import jwt
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
def verify_token(credentials):
"""Проверка токена — ошибка авторизации если токен невалидный"""
try:
token = credentials.credentials
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user_id
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired", # Ошибка авторизации!
headers={"WWW-Authenticate": "Bearer"},
)
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token", # Ошибка авторизации!
headers={"WWW-Authenticate": "Bearer"},
)
@app.get("/protected")
async def protected_route(user_id: str = Depends(verify_token)):
return {"message": f"Hello, user {user_id}"}
# Сценарии ошибки авторизации:
# 1. GET /protected → 401 (нет токена)
# 2. GET /protected?token=invalid → 401 (невалидный токен)
# 3. GET /protected?token=expired → 401 (истёкший токен)
HTTP код: 401 Unauthorized
from fastapi import HTTPException, status
@app.get("/api/data")
async def get_data(token: str = None):
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authentication token"
)
# Проверка валидности токена
if not is_valid_token(token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token"
)
return {"data": "sensitive"}
2. Недостаточные права (Authorization failure) — 403 Forbidden
Ошибка недостаточных прав возникает, когда система знает, кто вы, но вам не разрешено выполнять это действие.
Это означает: система знает, кто вы, но не доверяет вам выполнять это.
Примеры ошибок недостаточных прав:
- Вы пользователь, а операция требует администратора
- Вы не владелец ресурса
- Ваш план подписки не включает эту функцию
- Ваша роль не имеет необходимых разрешений
- Доступ к ресурсу запрещён политикой
from enum import Enum
from typing import List
class UserRole(str, Enum):
ADMIN = "admin"
MODERATOR = "moderator"
USER = "user"
class User:
def __init__(self, id: str, name: str, role: UserRole):
self.id = id
self.name = name
self.role = role
# Текущий пользователь (уже аутентифицирован)
current_user = User("123", "John", UserRole.USER)
def require_admin(user: User):
"""Проверка прав — ошибка авторизации если прав недостаточно"""
if user.role != UserRole.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Insufficient permissions. Admin role required."
)
return user
@app.delete("/admin/users/{user_id}")
async def delete_user(user_id: str, admin: User = Depends(require_admin)):
# Если пользователь — не админ, получит 403 Forbidden
return {"deleted": user_id}
# Сценарий:
# 1. Пользователь с ролью USER попытается удалить пользователя
# 2. Система знает, кто это (аутентификация ✅)
# 3. Но у него нет прав (авторизация ❌)
# 4. Ответ: 403 Forbidden
HTTP код: 403 Forbidden
@app.get("/api/admin/stats")
async def admin_stats(user_id: str = Depends(verify_token)):
user = get_user(user_id)
# Аутентификация успешна (мы знаем пользователя)
# Но проверяем права:
if user.role != "admin":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You don't have permission to access admin stats"
)
return {"stats": "admin data"}
Таблица сравнения
| Аспект | Авторизация (401) | Недостаточные права (403) |
|---|---|---|
| Вопрос | Кто вы? | Что вы можете делать? |
| Проблема | Система не знает вашу личность | Система знает вас, но запретила действие |
| Причина | Токен отсутствует, истёк, невалидный | Нет нужной роли/разрешения |
| HTTP код | 401 Unauthorized | 403 Forbidden |
| Решение | Аутентифицируйтесь (войдите) | Запросите доступ или свяжитесь с администратором |
| Заголовки | WWW-Authenticate | нет |
| Пример | JWT токен истёк | Вы не администратор |
Практический пример: API эндпоинты
from fastapi import APIRouter, HTTPException, status, Depends
from fastapi.security import HTTPBearer
router = APIRouter(prefix="/api/v1")
security = HTTPBearer()
class Post:
def __init__(self, id: str, owner_id: str, content: str):
self.id = id
self.owner_id = owner_id
self.content = content
# Фейковая БД
posts = {
"1": Post("1", "alice", "Alice's post"),
"2": Post("2", "bob", "Bob's post"),
}
def get_current_user(credentials) -> str:
"""Получить текущего пользователя (аутентификация)"""
token = credentials.credentials
# Проверка токена
if not token.startswith("user_"):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token", # 401 — не знаем, кто вы
)
user_id = token.replace("user_", "")
return user_id
@router.delete("/posts/{post_id}")
async def delete_post(
post_id: str,
user_id: str = Depends(get_current_user)
):
"""Удалить пост"""
# Сценарий 1: Токен отсутствует или невалиден
# → 401 Unauthorized (ошибка авторизации)
# Зависимость get_current_user выбросит исключение
post = posts.get(post_id)
if not post:
return {"error": "Post not found"}
# Сценарий 2: Токен валиден, но пользователь не владелец
if post.owner_id != user_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You can only delete your own posts" # 403 — прав недостаточно
)
# Сценарий 3: Успешно
del posts[post_id]
return {"deleted": post_id}
# Примеры запросов:
# 1. curl -H "Authorization: Bearer missing" http://localhost:8000/api/v1/posts/1
# → 401 Unauthorized (отсутствует валидный токен)
#
# 2. curl -H "Authorization: Bearer user_bob" http://localhost:8000/api/v1/posts/1
# → 403 Forbidden (Bob не владелец поста Alice)
#
# 3. curl -H "Authorization: Bearer user_alice" http://localhost:8000/api/v1/posts/1
# → 200 OK (Alice может удалить свой пост)
Лучшие практики
Всегда отличай ошибки:
from typing import Optional
class AuthenticationError(Exception):
"""401 — проблема с идентификацией"""
pass
class AuthorizationError(Exception):
"""403 — проблема с правами"""
pass
def secure_operation(user_id: Optional[str], action: str):
# 1. Проверка аутентификации
if not user_id:
raise AuthenticationError("User not authenticated") # 401
# 2. Проверка авторизации
user = get_user(user_id)
if action == "delete_user" and user.role != "admin":
raise AuthorizationError("Admin role required") # 403
# 3. Выполнение операции
return perform_action(user_id, action)
# Обработка в Flask/FastAPI:
@app.errorhandler(AuthenticationError)
def handle_auth_error(e):
return {"error": str(e)}, 401
@app.errorhandler(AuthorizationError)
def handle_authz_error(e):
return {"error": str(e)}, 403
Заключение
- 401 Unauthorized — система не знает вашу личность (нет токена, истёк, невалидный)
- 403 Forbidden — система знает вас, но вам запретили (нет прав)
- Оба нужны для безопасности, но служат разным целям
- Всегда проверяй сначала аутентификацию, потом авторизацию