JavaRush /Курси /Модуль 3: Django /Аутентифікація та дозволи в GraphQL

Аутентифікація та дозволи в GraphQL

Модуль 3: Django
Рівень 24 , Лекція 6
Відкрита

Вітаю на одній з найцікавіших і найкорисніших лекцій на нашому шляху до створення просунутого і безпечного GraphQL API. Сьогодні ми розберемо, як керувати доступом до даних, забезпечувати безпеку запитів та інтегрувати вбудовану систему аутентифікації Django у твій GraphQL проєкт.

На минулих лекціях ми познайомились з основами GraphQL, вивчили його основні переваги та відмінності від REST API. Ми встановили і налаштували Graphene-Django, створили першу схему (schema) і розібрались із запитами (queries) та мутаціями (mutations). Більше того, ми інтегрували Django моделі з GraphQL, використовуючи DjangoObjectType. Наш GraphQL API вже вміє повертати та змінювати дані з бази, але ми поки що не обмежували доступ до них для різних користувачів. Час це виправити!

Безпека та аутентифікація в GraphQL

Будь-який веб-додаток, чи то REST або GraphQL API, потребує захисту. Ми повинні контролювати, хто має доступ до даних, і обмежувати це залежно від ролі чи статусу користувача.

  • Навіщо потрібна аутентифікація в GraphQL?

Уяви, що твій GraphQL API — це двері в твій дім. Аутентифікація — це ключ, який підтверджує, що "цей користувач має право увійти". Без ключа сторонні можуть отримати доступ до даних, які їм не належать, а це, м'яко кажучи, погана ідея.

  • Як працює система аутентифікації в Django?

Django надає вбудовану систему аутентифікації, засновану на моделі User. До неї входять такі функції, як вхід у систему, вихід, управління користувачами та перевірка їхніх прав.

Graphene-Django прекрасно інтегрується з цією системою, надаючи нам інструменти для перевірки аутентифікованості запитів.

Інтеграція аутентифікації Django з GraphQL

Почнемо зі звичного сценарію: нам потрібно обмежити доступ до деяких запитів і мутацій лише для аутентифікованих користувачів. Давай розберемося, як це зробити.

Встановлення GraphQLAuth (опціонально)

Якщо ти хочеш пришвидшити процес інтеграції аутентифікації, встанови бібліотеку django-graphql-auth. Вона додає підтримку реєстрації, входу, виходу, зміни пароля та багато іншого "з коробки".

pip install django-graphql-auth
pip install django-graphql-jwt  # для обробки JWT токенів
pip install django-filter

Оновимо налаштування проєкту у settings.py:

INSTALLED_APPS += [
    "graphql_auth",
    "graphql_jwt.refresh_token",
    "django_filters",
]

AUTHENTICATION_BACKENDS = [
    "graphql_jwt.backends.JSONWebTokenBackend",
    "django.contrib.auth.backends.ModelBackend",
]

GRAPHENE = {
    "SCHEMA": "your_project.schema.schema",
    "MIDDLEWARE": [
        "graphql_jwt.middleware.JSONWebTokenMiddleware",
    ],
}

Тепер можна додати стандартний набір мутацій для аутентифікації:

import graphql_jwt
import graphql_auth.schema
from graphene import ObjectType

class AuthMutation(graphql_auth.schema.Mutation, ObjectType):
    pass

class Mutation(AuthMutation, ObjectType):
    token_auth = graphql_jwt.ObtainJSONWebToken.Field()
    verify_token = graphql_jwt.Verify.Field()
    refresh_token = graphql_jwt.Refresh.Field()

class Query(graphql_auth.schema.Query, ObjectType):
    pass

Ти щойно додав GraphQL мутації для отримання JWT токенів та перевірки їхньої дійсності.

Обмеження доступу за допомогою @login_required

Graphene надає простий декоратор @login_required, який можна використовувати для обмеження доступу до конкретних запитів або мутацій.

Приклад:

from graphene import Mutation, String
from graphql_jwt.decorators import login_required

class ProtectedMutation(Mutation):
    message = String()

    @login_required
    def mutate(self, info):
        return ProtectedMutation(message="Доступ відкрито лише для аутентифікованих користувачів.")

Тепер, якщо неаутентифікований користувач спробує виконати цю мутацію, він отримає повідомлення про помилку.

Управління доступом до даних

Тепер давай поговоримо про дозволи (permissions). Аутентифікація перевіряє, хто ти, а дозволи визначають, що тобі дозволено робити.

Django надає нам зручний механізм перевірки дозволів на рівні моделі. Наприклад, у моделі може бути дозвіл can_view, який дає право на перегляд даних.

Допустимо, у нас є модель Post:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)

    class Meta:
        permissions = [
            ("can_view", "Може переглядати пости"),
        ]

Тепер ми можемо додати перевірку дозволів у наші GraphQL запити:

from graphql_jwt.decorators import permission_required

class Query(ObjectType):
    post_detail = Field(PostType, id=ID(required=True))

    @permission_required('app_name.can_view')
    def resolve_post_detail(self, info, id):
        return Post.objects.get(id=id)

Цей запит буде доступний тільки користувачам із дозволом can_view. Якщо у користувача немає такого дозволу, він отримає помилку.

Кастомізація дозволів

Іноді вбудованих дозволів недостатньо, і нам потрібно написати свої. Наприклад, ми хочемо, щоб тільки власники поста могли його редагувати.

Приклад кастомного дозволу:

from graphql import GraphQLError

class PostOwnerPermission:
    @staticmethod
    def has_permission(info, post):
        if info.context.user != post.owner:
            raise GraphQLError("Ви не є власником цього поста.")
        return True

Тепер використовуємо цей дозвіл у мутації:

class UpdatePost(Mutation):
    class Arguments:
        id = ID(required=True)
        title = String()
        content = String()

    post = Field(PostType)

    def mutate(self, info, id, title=None, content=None):
        post = Post.objects.get(id=id)
        PostOwnerPermission.has_permission(info, post)

        if title:
            post.title = title
        if content:
            post.content = content
        post.save()
        return UpdatePost(post=post)

Приклади налаштування дозволів на рівні схеми

Іноді простіше задати загальну політику безпеки для всієї схеми, ніж перевіряти доступ для кожного запиту чи мутації.

Щоб обмежити доступ до всієї GraphQL схеми, ми можемо використовувати middleware:

class OnlyAuthenticatedMiddleware:
    def resolve(self, next, root, info, **args):
        if not info.context.user.is_authenticated:
            raise Exception("Будь ласка, увійдіть у систему, щоб скористатися GraphQL API.")
        return next(root, info, **args)

GRAPHENE["MIDDLEWARE"] += [OnlyAuthenticatedMiddleware()]

Цей middleware перевіряє аутентифікацію для всіх запитів.

Приклади помилок та їх вирішення

  • Помилка "AnonymousUser is not authenticated": перевір, чи встановлено налаштування MIDDLEWARE у settings.py. Не забудь про GRAPHENE["MIDDLEWARE"].

  • "Пропущено токен у заголовку запиту": переконайся, що ти передаєш токен у заголовку Authorization під час виконання запитів.

  • "GraphQLError не обробляється": використовуй правильні класи помилок. Наприклад, для перевірки дозволів використовуй GraphQLError, а не звичайний Exception.

Тепер твій GraphQL API вміє перевіряти, хто звертається до даних, і віддає їх лише тим, хто має право їх бачити. Ти піднявся на рівень вище в мистецтві створення захищених систем!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ