2. Request в Flask(RESTful API с Flask) и способы аутентификации пользователей
Request в Flask
Request (запрос) - это объект, который содержит всю информацию о входящем HTTP-запросе. Flask автоматически создает этот объект для каждого запроса. Основные атрибуты request:
request.method- HTTP метод (GET, POST, PUT, DELETE)request.args- параметры GET запросаrequest.form- данные формыrequest.files- загруженные файлыrequest.json- JSON данныеrequest.headers- HTTP заголовки
Обработка GET запроса
# Импорт необходимых компонентов из Flask
from flask import Flask, request
# Создание экземпляра приложения Flask
app = Flask(__name__)
# Декоратор @app.route определяет URL-маршрут '/search'
# Когда пользователь обращается к этому URL, вызывается функция search()
@app.route('/search')
def search():
# request.args.get('q', '') получает параметр 'q' из URL
# Если параметр не указан, возвращается пустая строка
query = request.args.get('q', '')
return f'Поисковый запрос: {query}'
Пример 2: Обработка POST запроса с формой
# Создаем словарь с пользователями (в реальном приложении это должна быть база данных)
users_db = {
'admin': {'password': 'admin123', 'role': 'admin'},
'user': {'password': 'user123', 'role': 'user'}
}
# Декоратор с указанием метода POST
# methods=['POST'] означает, что этот маршрут обрабатывает только POST запросы
@app.route('/login', methods=['POST'])
def login():
# request.form.get() получает данные из формы
# 'username' и 'password' - это имена полей в HTML форме
username = request.form.get('username')
password = request.form.get('password')
# Проверяем существование пользователя
if username in users_db and users_db[username]['password'] == password:
return jsonify({
'message': 'Успешный вход',
'user': username,
'role': users_db[username]['role']
})
else:
return jsonify({'error': 'Неверные учетные данные'}), 401
Пример 3: Обработка JSON данных
# Обработка POST запроса с JSON данными
@app.route('/api/data', methods=['POST'])
def receive_data():
# request.json автоматически парсит JSON данные из тела запроса
data = request.json
return f'Получены данные: {data}'
curl — универсальный инструмент командной строки, используемый для передачи данных между клиентом и сервером через различные протоколы. Он поддерживает множество протоколов, таких как HTTP, HTTPS, FTP, SFTP, SMTP, IMAP и другие, используется разработчиками, системными администраторами и тестировщиками.
Описание основных параметров curl
-Xили--request- указывает HTTP метод запроса (GET, POST, PUT, DELETE)-Hили--header- добавляет заголовки HTTP запроса-dили--data- отправляет данные в теле запроса-iили--include- показывает заголовки ответа-vили--verbose- показывает подробную информацию о запросе-bили--cookie- отправляет cookies-cили--cookie-jar- сохраняет cookies в файл-uили--user- указывает данные для базовой аутентификации-kили--insecure- игнорирует проверку SSL-сертификата-Lили--location- следует за перенаправлениями
Примеры запросов:
- GET запрос к поиску:
# -X GET указывает метод запроса
# URL содержит параметр q=python
curl -X GET "http://localhost:5000/search?q=python"
- POST запрос для входа пользователя:
# -H добавляет заголовок Content-Type
# -d отправляет данные формы
curl -X POST "http://localhost:5000/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin123"
CRUD операции в Flask
CRUD (Create, Read, Update, Delete) - это базовые операции для работы с данными. Рассмотрим их реализацию на примере простой системы управления задачами.
Создание (Create)
# Создаем список для хранения задач
tasks = []
# Обработка POST запроса для создания новой задачи
@app.route('/tasks', methods=['POST'])
def create_task():
# Получаем JSON данные из запроса
task_data = request.json
# Добавляем новую задачу в список
tasks.append(task_data)
# Возвращаем JSON ответ с сообщением и созданной задачей
return jsonify({'message': 'Задача создана', 'task': task_data})
Чтение (Read)
# Получение списка всех задач
@app.route('/tasks', methods=['GET'])
def get_tasks():
# Возвращаем все задачи в формате JSON
return jsonify({'tasks': tasks})
# Получение конкретной задачи по ID
@app.route('/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
# Поиск задачи по ID с помощью генератора
# next() возвращает первый найденный элемент или None
task = next((t for t in tasks if t['id'] == task_id), None)
if task:
return jsonify(task)
# Если задача не найдена, возвращаем ошибку 404
return jsonify({'error': 'Задача не найдена'}), 404
Обновление и удаление (Update, Delete)
# Обновление существующей задачи
@app.route('/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
# Получаем новые данные задачи
task_data = request.json
# Ищем задачу по ID и обновляем её
for i, task in enumerate(tasks):
if task['id'] == task_id:
tasks[i] = task_data
return jsonify({'message': 'Задача обновлена'})
return jsonify({'error': 'Задача не найдена'}), 404
# Удаление задачи
@app.route('/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
# Ищем задачу по ID и удаляем её
for i, task in enumerate(tasks):
if task['id'] == task_id:
del tasks[i]
return jsonify({'message': 'Задача удалена'})
return jsonify({'error': 'Задача не найдена'}), 404
- Создание новой задачи:
# -H указывает, что отправляем JSON
# -d содержит JSON данные
curl -X POST "http://localhost:5000/tasks" \
-H "Content-Type: application/json" \
-d '{"id": 1, "title": "Новая задача", "description": "Описание задачи"}'
- Получение списка задач:
# Простой GET запрос без дополнительных параметров
curl -X GET "http://localhost:5000/tasks"
- Получение конкретной задачи:
# В URL указываем ID задачи
curl -X GET "http://localhost:5000/tasks/1"
- Обновление задачи:
# PUT запрос с JSON данными
curl -X PUT "http://localhost:5000/tasks/1" \
-H "Content-Type: application/json" \
-d '{"id": 1, "title": "Обновленная задача", "description": "Новое описание"}'
- Удаление задачи:
# DELETE запрос с указанием ID задачи
curl -X DELETE "http://localhost:5000/tasks/1"
RESTful API в Flask
REST (Representational State Transfer) - это архитектурный стиль для создания веб-сервисов. RESTful API следует определенным принципам:
- Использование HTTP методов для операций
- Использование URL для идентификации ресурсов
- Использование JSON для обмена данными
- Статусные коды HTTP для индикации результата
Базовый RESTful API для пользователей
# Создаем список для хранения пользователей
users = []
# Получение списка всех пользователей
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify({'users': users})
# Создание нового пользователя
@app.route('/api/users', methods=['POST'])
def create_user():
# Получаем данные пользователя из JSON
user_data = request.json
# Добавляем пользователя в список
users.append(user_data)
# Возвращаем созданного пользователя и статус 201 (Created)
return jsonify(user_data), 201
Создание нового пользователя через API:
# POST запрос с JSON данными пользователя
curl -X POST "http://localhost:5000/api/users" \
-H "Content-Type: application/json" \
-d '{"id": 1, "username": "newuser", "email": "newuser@example.com"}'
Получение информации о пользователе:
# GET запрос с ID пользователя
curl -X GET "http://localhost:5000/api/users/1"
Обработка конкретного пользователя
# Получение информации о конкретном пользователе
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
# Поиск пользователя по ID
user = next((u for u in users if u['id'] == user_id), None)
if user:
return jsonify(user)
# Если пользователь не найден, возвращаем ошибку 404
return jsonify({'error': 'Пользователь не найден'}), 404
Вложенные ресурсы
# Получение постов конкретного пользователя
@app.route('/api/users/<int:user_id>/posts', methods=['GET'])
def get_user_posts(user_id):
# Фильтруем посты по ID пользователя
user_posts = [p for p in posts if p['user_id'] == user_id]
return jsonify({'posts': user_posts})
Получение постов пользователя:
# GET запрос к вложенному ресурсу
curl -X GET "http://localhost:5000/api/users/1/posts"
Способы аутентификации пользователей
аутентификацией и авторизацией Эти два термина часто путают, так как они тесно связаны с процессом проверки прав доступа пользователя в системе. Однако их функции и цели различны.
Аутентификация (Authentication)
Аутентификация — это процесс подтверждения личности пользователя. Система проверяет, что пользователь действительно тот, за кого себя выдает.
Убедиться, что пользователь является тем, за кого он себя выдает.
Примеры: - Ввод имени пользователя и пароля. - Использование двухфакторной аутентификации (2FA), например, через SMS или приложение для генерации кодов. - Биометрическая аутентификация (отпечаток пальца, распознавание лица). - Аутентификация через OAuth (например, вход через Google или Facebook).
Авторизация (Authorization)
Авторизация — это процесс определения прав доступа пользователя после того, как его личность подтверждена. Система решает, какие ресурсы или действия доступны пользователю.
Определить, что пользователь может делать в системе.
Примеры: - Пользователь с ролью "администратор" может управлять контентом сайта, а пользователь с ролью "гость" — только просматривать его. - Приложение запрашивает доступ к вашим контактам или фотографиям в социальной сети (например, через OAuth). - Доступ к определенным файлам или директориям на сервере.
Реализация в Flask
JWT (JSON Web Token) — это стандарт формата токенов, используемый для безопасной передачи информации между сторонами в виде JSON-объекта. JWT часто применяется для аутентификации и авторизации пользователей в веб-приложениях.
Установка Flask и PyJWT
pip install Flask PyJWT
from flask import Flask, request, jsonify
import jwt
import datetime
app = Flask(__name__)
# Секретный ключ для подписи JWT
SECRET_KEY = "mysecretkey"
# Простая база данных пользователей (в реальном приложении используйте БД)
users = {
"user1": "password1",
"user2": "password2"
}
# Функция для создания JWT
def create_jwt(username):
payload = {
"username": username,
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1) # Токен действителен 1 час
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
return token
# Функция для проверки JWT
def verify_jwt(token):
try:
# Расшифровываем токен
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload["username"]
except jwt.ExpiredSignatureError:
return "Token expired", 401
except jwt.InvalidTokenError:
return "Invalid token", 401
# Маршрут для входа пользователя
@app.route("/login", methods=["POST"])
def login():
data = request.get_json()
username = data.get("username")
password = data.get("password")
# Проверяем, есть ли пользователь в базе данных
if username in users and users[username] == password:
# Создаем JWT
token = create_jwt(username)
return jsonify({"token": token}), 200
else:
return jsonify({"message": "Invalid credentials"}), 401
# Защищенный маршрут (требует JWT)
@app.route("/protected", methods=["GET"])
def protected():
# Получаем токен из заголовков запроса
auth_header = request.headers.get("Authorization")
if not auth_header:
return jsonify({"message": "Missing token"}), 401
# Извлекаем токен из заголовка (Bearer <token>)
token = auth_header.split(" ")[1]
# Проверяем токен
result = verify_jwt(token)
if isinstance(result, tuple): # Если вернулась ошибка
return result
# Если токен валиден, возвращаем защищенные данные
return jsonify({"message": f"Hello, {result}! This is a protected route."}), 200
if __name__ == "__main__":
app.run(debug=True)
-
Функция create_jwt создает токен, который содержит следующие данные:
username: имя пользователя. exp: время истечения действия токена (в данном случае 1 час).
Токен подписывается секретным ключом (SECRET_KEY) с использованием алгоритма HMAC SHA-256 (HS256). Подпись гарантирует, что токен не был изменен после его создания. 2. Проверка JWT
Функция verify_jwt проверяет токен:
Если токен истек (ExpiredSignatureError), возвращается ошибка 401.
Если токен невалиден (InvalidTokenError), также возвращается ошибка 401.
Если токен валиден, извлекается имя пользователя.
- Маршрут /login принимает логин и пароль пользователя через POST-запрос. Если учетные данные верны, создается JWT и возвращается клиенту.
Проверка через curl
curl -X POST http://127.0.0.1:5000/login \
-H "Content-Type: application/json" \
-d '{"username": "user1", "password": "password1"}'
OAuth (Open Authorization) — это стандартный протокол для авторизации, который позволяет пользователям предоставлять доступ к их данным на одном сервисе другому сервису без необходимости делиться учетными данными. В отличие от JWT, OAuth фокусируется на управлении доступом через токены и обычно используется для интеграции с внешними провайдерами (например, Google, Facebook, GitHub).
Установка Flask и Authlib:
pip install Flask Authlib
Для работы с OAuth вам нужно зарегистрировать приложение в выбранном провайдере (например, Google). После регистрации вы получите: - Client ID — уникальный идентификатор вашего приложения. - Client Secret — секретный ключ для вашего приложения.
Пример настройки Google OAuth
1. Перейдите в Google Cloud Console.
2. Создайте проект и включите API "Google OAuth 2.0".
3. Зарегистрируйте ваше приложение, указав URI перенаправления (например, http://localhost:5000/callback).
4. Получите Client ID и Client Secret.
Пример
from flask import Flask, redirect, url_for, session, request, jsonify
from authlib.integrations.flask_client import OAuth
import os
app = Flask(__name__)
# Секретный ключ для сессий
app.secret_key = os.getenv("SECRET_KEY", "supersecretkey")
# Инициализация OAuth
oauth = OAuth(app)
# Настройка Google OAuth
google = oauth.register(
name="google",
client_id=os.getenv("GOOGLE_CLIENT_ID", "your-client-id"),
client_secret=os.getenv("GOOGLE_CLIENT_SECRET", "your-client-secret"),
access_token_url="https://accounts.google.com/o/oauth2/token",
authorize_url="https://accounts.google.com/o/oauth2/auth",
api_base_url="https://www.googleapis.com/oauth2/v1/",
client_kwargs={"scope": "openid email profile"},
)
# Маршрут для входа через Google
@app.route("/login")
def login():
redirect_uri = url_for("callback", _external=True)
return google.authorize_redirect(redirect_uri)
# Маршрут для обработки callback от Google
@app.route("/callback")
def callback():
token = google.authorize_access_token()
user_info = google.get("userinfo").json()
# Сохраняем информацию о пользователе в сессии
session["user"] = user_info
return redirect(url_for("profile"))
# Защищенный маршрут (требует авторизации)
@app.route("/profile")
def profile():
user = session.get("user")
if not user:
return redirect(url_for("login"))
return jsonify(user)
# Маршрут для выхода
@app.route("/logout")
def logout():
session.pop("user", None)
return jsonify({"message": "Logged out successfully"})
if __name__ == "__main__":
app.run(debug=True)
Мы используем библиотеку Authlib для работы с OAuth. В методе oauth.register указываются параметры для подключения к Google OAuth:
- client_id и client_secret: данные, полученные при регистрации приложения.
- authorize_url: URL для запроса авторизации у пользователя.
- access_token_url: URL для получения токена доступа.
- api_base_url: базовый URL для API провайдера.
- scope: запрашиваемые разрешения (например, доступ к email и профилю).
Маршрут /loginперенаправляет пользователя на страницу авторизации Google. После успешной авторизации Google перенаправит пользователя на маршрут /callback.
Маршрут /callback после авторизации Google отправляет токен доступа на этот маршрут. Мы используем его для получения информации о пользователе (например, email и имя) через API Google.
Маршрут /profile доступен только для авторизованных пользователей. Если пользователь не авторизован, он будет перенаправлен на страницу входа.
Маршрут /logout удаляет информацию о пользователе из сессии, завершая сеанс.
Рекомендации по испольованию данного подхода
- OAuth требует использования HTTPS для защиты данных.
- Не храните Client ID, Client Secret и другие конфиденциальные данные в коде. Используйте переменные окружения или секретные хранилища.
- Добавьте обработку ошибок OAuth (например, если пользователь отклоняет запрос авторизации).
Практические задания
- Проверьте из примера про JWT защищенный маршут через curl.
- OAuth реазизуйте пример с други провайдером.
Дальнейшие темы для изучения - Куки, сессии - Ролевая модель (Role-Based Access Control, RBAC). - Проверка прав доступа на уровне API (например, middleware в Flask или Django). - ACL (Access Control Lists) для точного контроля доступа. - Подключение к базе данных, SQLAlchemy - Pydentic
Дополнительная литература