← Назад к вопросам
Как получить токен на клиенте?
2.0 Middle🔥 211 комментариев
#REST API и HTTP#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как получить токен на клиенте
Получение токена — фундаментальная операция при работе с API. Я расскажу про все варианты: от простых до enterprise решений.
1. Базовая аутентификация: username + password
На сервере (FastAPI):
from fastapi import FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from datetime import datetime, timedelta, timezone
import jwt
app = FastAPI()
security = HTTPBasic()
SECRET_KEY = "your-secret-key-change-in-prod"
ALGORITHM = "HS256"
@app.post("/api/v1/auth/token")
async def login(credentials: HTTPBasicCredentials):
"""Получить JWT токен по username и password."""
# Проверяем учётные данные (в реальности — ищем в БД)
user = authenticate_user(credentials.username, credentials.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Неверные учётные данные",
headers={"WWW-Authenticate": "Bearer"},
)
# Генерируем JWT токен
expire = datetime.now(timezone.utc) + timedelta(hours=1)
payload = {
"sub": user.id,
"username": user.username,
"exp": expire
}
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
return {
"access_token": token,
"token_type": "bearer",
"expires_in": 3600
}
На клиенте (Python requests):
import requests
from requests.auth import HTTPBasicAuth
# Способ 1: передача credentials в заголовок
response = requests.post(
"https://api.example.com/api/v1/auth/token",
auth=HTTPBasicAuth("username", "password")
)
token_data = response.json()
token = token_data["access_token"]
print(f"Получен токен: {token}")
# Способ 2: передача в body (чаще используется)
response = requests.post(
"https://api.example.com/api/v1/auth/token",
data={
"username": "user@example.com",
"password": "password123"
}
)
if response.status_code == 200:
token = response.json()["access_token"]
else:
raise Exception(f"Ошибка аутентификации: {response.text}")
2. OAuth2 с Bearer токеном
На сервере (FastAPI с OAuth2):
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
import jwt
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class Token(BaseModel):
access_token: str
token_type: str
class User(BaseModel):
username: str
email: str
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
"""Получить токен по OAuth2 Password Flow."""
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Неверные учётные данные"
)
access_token = create_access_token(user.id)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/me", response_model=User)
async def read_users_me(token: str = Depends(oauth2_scheme)):
"""Получить текущего пользователя по токену."""
user = verify_token(token)
return user
На клиенте:
import requests
from requests.auth import HTTPBearer
# Шаг 1: получить токен
response = requests.post(
"https://api.example.com/token",
data={
"username": "user@example.com",
"password": "password123"
}
)
token = response.json()["access_token"]
# Шаг 2: использовать токен в заголовке Authorization
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(
"https://api.example.com/me",
headers=headers
)
user = response.json()
print(user) # {"username": "...", "email": "..."}
3. API Key (простой токен)
На сервере:
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
VALID_API_KEYS = {
"sk_test_1234567890": {"user_id": 1, "permissions": ["read", "write"]}
}
@app.get("/api/v1/data")
async def get_data(x_api_key: str = Header()):
"""Эндпоинт, защищённый API Key."""
if x_api_key not in VALID_API_KEYS:
raise HTTPException(
status_code=403,
detail="Неверный API ключ"
)
api_key_info = VALID_API_KEYS[x_api_key]
return {"data": "secret", "user_id": api_key_info["user_id"]}
На клиенте:
import requests
api_key = "sk_test_1234567890"
headers = {"X-API-Key": api_key}
response = requests.get(
"https://api.example.com/api/v1/data",
headers=headers
)
print(response.json())
4. Refresh токены для долговечных сессий
На сервере:
from datetime import datetime, timedelta, timezone
import jwt
SECRET_KEY = "secret"
ACCESS_TOKEN_EXPIRE = timedelta(minutes=15)
REFRESH_TOKEN_EXPIRE = timedelta(days=7)
def create_tokens(user_id: str) -> dict:
"""Создаёт access и refresh токены."""
# Access token (короткоживущий)
access_expire = datetime.now(timezone.utc) + ACCESS_TOKEN_EXPIRE
access_payload = {"sub": user_id, "type": "access", "exp": access_expire}
access_token = jwt.encode(access_payload, SECRET_KEY, algorithm="HS256")
# Refresh token (долгоживущий)
refresh_expire = datetime.now(timezone.utc) + REFRESH_TOKEN_EXPIRE
refresh_payload = {"sub": user_id, "type": "refresh", "exp": refresh_expire}
refresh_token = jwt.encode(refresh_payload, SECRET_KEY, algorithm="HS256")
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
"expires_in": int(ACCESS_TOKEN_EXPIRE.total_seconds())
}
@app.post("/token")
async def login(username: str, password: str):
"""Получить access и refresh токены."""
user = authenticate_user(username, password)
if not user:
raise HTTPException(status_code=401)
return create_tokens(user.id)
@app.post("/token/refresh")
async def refresh_token(refresh_token: str):
"""Получить новый access токен по refresh токену."""
try:
payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=["HS256"])
if payload.get("type") != "refresh":
raise HTTPException(status_code=401)
user_id = payload["sub"]
new_tokens = create_tokens(user_id)
return new_tokens
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Refresh токен истёк")
На клиенте:
import requests
import json
import os
class AuthClient:
def __init__(self, api_url: str):
self.api_url = api_url
self.token_file = "tokens.json"
def login(self, username: str, password: str):
"""Получить токены и сохранить их."""
response = requests.post(
f"{self.api_url}/token",
data={"username": username, "password": password}
)
tokens = response.json()
self._save_tokens(tokens)
return tokens
def get_access_token(self) -> str:
"""Получить действующий access токен."""
tokens = self._load_tokens()
# Если токен истёк, обновляем
if self._is_token_expired(tokens["access_token"]):
self.refresh_tokens()
tokens = self._load_tokens()
return tokens["access_token"]
def refresh_tokens(self):
"""Обновить access токен используя refresh токен."""
tokens = self._load_tokens()
response = requests.post(
f"{self.api_url}/token/refresh",
data={"refresh_token": tokens["refresh_token"]}
)
new_tokens = response.json()
self._save_tokens(new_tokens)
def get_headers(self) -> dict:
"""Получить заголовки с токеном для API запроса."""
token = self.get_access_token()
return {"Authorization": f"Bearer {token}"}
def _save_tokens(self, tokens: dict):
with open(self.token_file, "w") as f:
json.dump(tokens, f)
def _load_tokens(self) -> dict:
with open(self.token_file, "r") as f:
return json.load(f)
def _is_token_expired(self, token: str) -> bool:
import jwt
try:
payload = jwt.decode(token, options={"verify_signature": False})
exp = payload.get("exp")
return datetime.fromtimestamp(exp) < datetime.now()
except:
return True
# Использование
auth = AuthClient("https://api.example.com")
auth.login("user@example.com", "password")
# При каждом запросе токен автоматически обновляется если нужно
response = requests.get(
"https://api.example.com/me",
headers=auth.get_headers()
)
5. OpenID Connect (для сложных сценариев)
from authlib.integrations.requests_client import OAuth2Session
client = OAuth2Session(
client_id="your-client-id",
client_secret="your-client-secret",
redirect_uri="http://localhost:8000/callback"
)
# Получить ссылку для аутентификации
uri, state = client.create_authorization_url(
"https://provider.com/authorize"
)
# После редиректа получить токен
token = client.fetch_token(
"https://provider.com/token",
authorization_response=callback_response
)
print(token["access_token"])
Рекомендации
| Сценарий | Решение |
|---|---|
| Простой API | API Key в заголовке |
| Web приложение | OAuth2 с refresh токеном |
| Mobile приложение | OAuth2 + PKCE |
| Микросервисы | JWT с долгоживущими токенами |
| Enterprise | OpenID Connect, SAML |
Вывод
- Никогда не передавай пароль в каждом запросе — получи токен один раз
- Используй HTTPS при передаче токенов
- Refresh токены для долговечных сессий
- Кэшируй токены локально чтобы не делать лишние запросы
- Проверяй истечение и автоматически обновляй если надо