Новая Школа - онлайн-школа подготовки к ЕГЭ
Задача Задача №3374076
А
Аноним
10 сентября 2025 12:23 · публичный вопрос
А
Аноним

дай мне код авторизации

Ответ нейросети
10 сентября 2025 12:23

Задача понятна: ты хочешь увидеть рабочий пример кода, реализующий процесс авторизации по схеме Authorization Code (OAuth 2.0), чтобы понять, как это работает на практике. Ниже даю минимальный учебный пример на Python (Flask) и пояснения по шагам. Это учебный макет, не для продакшена (нет TLS, полноценных проверок и т.д.).

Цель по твоему запросу: Понять. Я сначала объясню концепцию, затем дам рабочий код и пояснения к каждому фрагменту.

Что мы сделаем

  • Реализуем простой «сервер авторизации» (Authorization Server) с двумя точками:
    • /authorize — клиент перенаправляется сюда, чтобы получить authorization code.
    • /token — клиент обменивает code на access token.
  • Реализуем небольшой «клиент» (CLI), который:
    • открывает браузер для авторизации,
    • после логина пользователя получает authorization code,
    • обменивает код на токен и выводит его.
  • Добавлю дополнительную точку /resource, чтобы проверить защиту токеном.

Установка

  • Убедись, что установлен Python 3.8+.
  • Установи Flask: pip install flask
  • (Опционально) установи requests: pip install requests
  1. Код сервера авторизации (authorization_server.py) Код ниже реализует простой OAuth2-совместимый сервер с двумя конечными точками и базовым хранилищем пользователей и клиентов.
  • Что делает:
    • /authorize: принимает параметры client_id, redirect_uri, response_type=code, state. Если пользователь не авторизован — отправляет форму входа. Если авторизован — создает authorization code и перенаправляет по redirect_uri с code и state.
    • /login: простая форма входа. После входа сохраняет в сессии пользователя и возвращает на исходную страницу.
    • /token: принимает code, client_id, client_secret, redirect_uri и возвращает access_token.
    • /resource: защищенный ресурс, доступный только по Bearer токену.

Код (authorization_server.py):

from flask import Flask, request, redirect, session, render_template_string, jsonify
import secrets, time

app = Flask(__name__)
app.secret_key = 'super_secret_key_for_demo'  # в продакшене хранить в env

# Простые данные пользователей и клиентов (для учебы)
USERS = {
    'alice': 'wonderland',
    'bob': 'builder'
}

CLIENTS = {
    'demo_client': {
        'client_secret': 'demo_secret',
        'redirect_uris': ['http://localhost:8000/callback']
    }
}

# Хранилища кодов и токенов
AUTH_CODES = {}     # code -> {client_id, redirect_uri, username, expires_at}
ACCESS_TOKENS = {}  # access_token -> {username, client_id, expires_at}

def generate_code():
    return secrets.token_urlsafe(32)

def generate_token():
    return secrets.token_urlsafe(48)

@app.route('/authorize')
def authorize():
    client_id = request.args.get('client_id')
    redirect_uri = request.args.get('redirect_uri')
    response_type = request.args.get('response_type')
    state = request.args.get('state')

    if client_id not in CLIENTS:
        return "Unknown client", 400
    if redirect_uri not in CLIENTS[client_id]['redirect_uris']:
        return "Invalid redirect_uri", 400
    if response_type != 'code':
        return "Unsupported response_type", 400

    if 'username' not in session:
        # перенаправим на страницу логина, сохранив исходник в next
        login_next = request.url
        return redirect(f'/login?next={login_next}')

    # пользователь авторизован — создаём authorization code
    code = generate_code()
    AUTH_CODES[code] = {
        'client_id': client_id,
        'redirect_uri': redirect_uri,
        'username': session['username'],
        'expires_at': time.time() + 300  # код действует 5 минут
    }

    # перенаправляем обратно к client с кодом
    redirect_with_code = f'{redirect_uri}?code={code}'
    if state:
        redirect_with_code += f'&state={state}'
    return redirect(redirect_with_code)

@app.route('/login', methods=['GET','POST'])
def login():
    if request.method == 'GET':
        next_url = request.args.get('next', '/')
        return render_template_string('''
            <h2>Login</h2>
            <form method="post">
              <input name="username" placeholder="Username"/><br/>
              <input type="password" name="password" placeholder="Password"/><br/>
              <input type="hidden" name="next" value="{{ next_url }}">
              <button type="submit">Login</button>
            </form>
        ''', next_url=next_url)

    username = request.form.get('username')
    password = request.form.get('password')
    next_url = request.form.get('next') or '/'

    if not username or USERS.get(username) != password:
        return "Invalid credentials", 401

    session['username'] = username
    return redirect(next_url)

@app.route('/token', methods=['POST'])
def token():
    grant_type = request.form.get('grant_type')
    code = request.form.get('code')
    redirect_uri = request.form.get('redirect_uri')
    client_id = request.form.get('client_id')
    client_secret = request.form.get('client_secret')

    if grant_type != 'authorization_code':
        return jsonify({'error':'unsupported_grant_type'}), 400
    if client_id not in CLIENTS:
        return jsonify({'error':'invalid_client'}), 401
    if CLIENTS[client_id]['client_secret'] != client_secret:
        return jsonify({'error':'invalid_client'}), 401
    if code not in AUTH_CODES:
        return jsonify({'error':'invalid_grant'}), 400

    code_entry = AUTH_CODES.pop(code)
    if code_entry['client_id'] != client_id:
        return jsonify({'error':'invalid_grant'}), 400
    if code_entry['redirect_uri'] != redirect_uri:
        return jsonify({'error':'invalid_grant'}), 400
    if time.time() > code_entry['expires_at']:
        return jsonify({'error':'invalid_grant'}), 400

    access_token = generate_token()
    ACCESS_TOKENS[access_token] = {
        'username': code_entry['username'],
        'client_id': client_id,
        'expires_at': time.time() + 3600
    }
    return jsonify({
        'access_token': access_token,
        'token_type': 'Bearer',
        'expires_in': 3600
    })

@app.route('/resource')
def resource():
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({'error':'unauthorized'}), 401
    token = auth_header.split()[1]
    token_entry = ACCESS_TOKENS.get(token)
    if not token_entry or token_entry['expires_at'] < time.time():
        return jsonify({'error':'invalid_token'}), 401

    username = token_entry['username']
    return jsonify({'data': f'Hello, {username}! This is protected data.'})

if __name__ == '__main__':
    app.run(debug=True, port=5000)
  1. Клиентская часть (CLI-демонстрация) (oauth_client.py) Этот клиент демонстрирует как получить authorization code через браузер, а затем обменять его на access token. Для упрощения не пишем локальный обработчик редиректа — пользователь вручную копирует URL с кодом и вставляет в консоль.
  • Что делает:
    • формирует URL авторизации и открывает в браузере
    • после логина пользователь копирует полный redirect URL из адресной строки и вставляет в консоль
    • извлекает code и отправляет запрос на /token
    • выводит access_token и пример запроса к защищенному ресурсу

Код (oauth_client.py):

import requests
import webbrowser
import urllib.parse
from urllib.parse import urlparse, parse_qs

SERVER = 'http://127.0.0.1:5000'
CLIENT_ID = 'demo_client'
CLIENT_SECRET = 'demo_secret'
REDIRECT_URI = 'http://localhost:8000/callback'  # произвольный заглушечный URL для примера

def main():
    # Шаг 1: построить URL авторизации
    authorize_url = (
        f"{SERVER}/authorize"
        f"?response_type=code&client_id={CLIENT_ID}"
        f"&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
        f"&state=xyz"
    )
    print("Откройте этот URL в браузере для авторизации (браузер откроется автоматически):")
    print(authorize_url)
    webbrowser.open(authorize_url)

    # Шаг 2: пользователь логинится на сервере авторизации,
    # затем копирует полный redirect URL из адресной строки и вставляет сюда
    redirected = input("Вставьте полный Redirect URL после авторизации: ").strip()
    parsed = urlparse(redirected)
    code = parse_qs(parsed.query).get('code')
    if not code:
        print("Не найден параметр code в redirect URL.")
        return
    code = code[0]

    # Шаг 3: обменяем code на access_token
    token_resp = requests.post(
        f"{SERVER}/token",
        data={
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': REDIRECT_URI,
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET
        }
    )
    if token_resp.status_code != 200:
        print("Ошибка обмена кода на токен:", token_resp.text)
        return

    token_data = token_resp.json()
    access_token = token_data['access_token']
    print("Получен access_token:", access_token)

    # Шаг 4: пример доступа к защищенному ресурсу
    resp = requests.get(
        f"{SERVER}/resource",
        headers={'Authorization': f"Bearer {access_token}"}
    )
    print("Ответ ресурса:", resp.status_code, resp.text)

if __name__ == '__main__':
    main()

Как запустить и поэтапно проверить

  • Запусти сервер авторизации:
    • python authorization_server.py
    • Он будет слушать на 127.0.0.1
  • Открой клиентскую часть:
    • Убедись, что в редирект URI клиента добавлен localhost в настройках клиента (в нашем примере это сделано в коде).
    • python oauth_client.py
    • Скрипт откроет браузер для авторизации. Введи учетку из USERS (alice/wonderland или bob/builder).
    • После входа в браузере адресная строка покажет редирект с параметрами code и state. Скопируй полный URL и вставь в консоль там, где просит программа.
    • Клиент обменяет code на токен и попытается обратиться к /resource, получив защищенные данные.

Пояснения по шагам

  • Что такое authorization_code?
    • Это одноразовый код, выданный пользователю после успешной аутентификации клиента и одобрения запрашиваемого доступа. Он служит временным мостом между сервером авторизации и клиентом.
  • Зачем нужен redirect_uri и state?
    • redirect_uri гарантирует, что код возвращается именно к нашему клиенту. state — дополнительный параметр защиты от CSRF; он можно использовать для валидации возвращенного запроса.
  • Как работают token и access_token?
    • После обмена кода на токен клиент получает access_token, который можно использовать для доступа к защищенным ресурсам. В реальной системе токен обычно JWT или opaque token и имеет срок жизни (expires_in).

Замечания и безопасность

  • Этот пример рассчитан на учебные цели. Он не включает HTTPS, полноценные проверки, защиту от CSRF, хранение секретов в безопасном месте и пр.
  • В продакшене обязательно используйте HTTPS, валидируйте все параметры, храните клиентские секреты в безопасном месте (например, секреты окружения), реализуйте дополнительные проверки и мониторинг.

Если хочешь, могу адаптировать пример под:

  • другой язык/фреймворк (Node.js, Django, FastAPI и т.д.)
  • полноценную oauth2-систему с поддержкой рефреш-токенов
  • упрощенную версию без Authorization Server (любой другой сценарий)

Хочешь, чтобы я адаптировал под конкретный язык или добавил UI для авторизации?