← Назад к вопросам
Как работает авторизация на сайте?
1.7 Middle🔥 171 комментариев
#Python Core#Soft Skills
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает авторизация на сайте
Авторизация — это процесс подтверждения того, что пользователь имеет доступ к определённым ресурсам. Это отличается от аутентификации (проверка личности) и аутентификации (выдача прав доступа).
Жизненный цикл авторизации
1. Регистрация
Пользователь создает учетную запись:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from passlib.context import CryptContext
import secrets
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class UserRegister(BaseModel):
email: EmailStr
password: str
name: str
@app.post("/api/v1/auth/register")
async def register(user_data: UserRegister):
# Проверяем, не зарегистрирован ли уже
existing = await db.get_user_by_email(user_data.email)
if existing:
return {"error": "Email already registered"}, 400
# Хешируем пароль (никогда не храним в открытом виде)
hashed_password = pwd_context.hash(user_data.password)
# Создаём пользователя
user = await db.create_user(
email=user_data.email,
password_hash=hashed_password,
name=user_data.name
)
return {"id": user.id, "email": user.email, "name": user.name}
2. Аутентификация (Логин)
Пользователь вводит учетные данные:
from datetime import datetime, timedelta
import jwt
from typing import Dict
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
class Login(BaseModel):
email: str
password: str
@app.post("/api/v1/auth/login")
async def login(credentials: Login):
# Получаем пользователя из БД
user = await db.get_user_by_email(credentials.email)
# Проверяем существование и пароль
if not user or not pwd_context.verify(credentials.password, user.password_hash):
return {"error": "Invalid email or password"}, 401
# Проверяем, активен ли пользователь
if not user.is_active:
return {"error": "Account disabled"}, 403
# Создаём JWT токены
access_token = create_access_token(user.id)
refresh_token = create_refresh_token(user.id)
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
"user": {"id": user.id, "email": user.email, "name": user.name}
}
def create_access_token(user_id: str) -> str:
"""Создать JWT access token с коротким сроком действия."""
payload = {
"sub": user_id,
"type": "access",
"exp": datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
"iat": datetime.utcnow(),
}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
def create_refresh_token(user_id: str) -> str:
"""Создать JWT refresh token с длительным сроком действия."""
payload = {
"sub": user_id,
"type": "refresh",
"exp": datetime.utcnow() + timedelta(days=7),
"iat": datetime.utcnow(),
}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
3. Использование Access Token
Клиент отправляет токен в заголовке Authorization:
// Frontend (JavaScript)
const response = await fetch('/api/v1/users/me', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const user = await response.json();
Bэкенд проверяет токен:
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict:
"""Проверить JWT токен."""
token = credentials.credentials
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id = payload.get("sub")
token_type = payload.get("type")
if not user_id or token_type != "access":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": user_id}
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/api/v1/users/me")
async def get_current_user(token: Dict = Depends(verify_token)):
user = await db.get_user_by_id(token["user_id"])
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {"id": user.id, "email": user.email, "name": user.name}
4. Обновление токена (Refresh)
Когда access token истекает, используем refresh:
@app.post("/api/v1/auth/refresh")
async def refresh_token(refresh_token: str):
try:
payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
user_id = payload.get("sub")
token_type = payload.get("type")
if not user_id or token_type != "refresh":
raise HTTPException(status_code=401, detail="Invalid refresh token")
# Проверяем в БД (если используем blacklist)
if not await db.refresh_token_exists(user_id, refresh_token):
raise HTTPException(status_code=401, detail="Refresh token revoked")
# Создаём новый access token
new_access = create_access_token(user_id)
return {
"access_token": new_access,
"token_type": "bearer"
}
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Refresh token expired")
5. Разделение прав доступа (Authorization)
Роли и права
from enum import Enum
from typing import List
class Role(str, Enum):
ADMIN = "admin"
MODERATOR = "moderator"
USER = "user"
def check_role(required_role: Role):
async def verify(token: Dict = Depends(verify_token)) -> Dict:
user = await db.get_user_by_id(token["user_id"])
if user.role != required_role and user.role != Role.ADMIN:
raise HTTPException(status_code=403, detail="Insufficient permissions")
return {"user_id": token["user_id"], "role": user.role}
return verify
# Использование
@app.delete("/api/v1/users/{user_id}")
async def delete_user(user_id: str, auth: Dict = Depends(check_role(Role.ADMIN))):
# Только админ может удалить пользователя
await db.delete_user(user_id)
return {"ok": True}
6. OAuth 2.0 / Social Login
Делегированная авторизация через третьих сторон:
from authlib.integrations.starlette_client import OAuth
oauth = OAuth()
google = oauth.register(
name='google',
client_id='YOUR_CLIENT_ID',
client_secret='YOUR_CLIENT_SECRET',
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'}
)
@app.get("/api/v1/auth/google/callback")
async def google_callback(request: Request):
token = await google.authorize_access_token(request)
user_info = token['userinfo']
# Создаём или обновляем пользователя
user = await db.get_or_create_user(
email=user_info['email'],
name=user_info['name'],
google_id=user_info['sub']
)
# Создаём токены
access = create_access_token(user.id)
refresh = create_refresh_token(user.id)
return {"access_token": access, "refresh_token": refresh}
Типичный workflow
- Регистрация → Пользователь создает аккаунт
- Логин → Проверяем пароль, выдаём access + refresh токены
- Запросы → Клиент отправляет access в заголовке Authorization
- Проверка → Backend проверяет токен и права доступа
- Обновление → При истечении access используем refresh для новой пары
- Логаут → Инвалидируем refresh token
Лучшие практики
- Никогда не храни пароли в открытом виде (используй bcrypt)
- Access токены — short-lived (15 мин)
- Refresh токены — long-lived (7 дней), хранятся в БД
- HTTPS — всегда, обязательно
- CSRF защита — для форм
- Rate limiting — на логин и регистрацию
- 2FA — для критичных операций