Řešení chyb přihlášení k subdoméně Django-Tenant pomocí tokenů Rest Framework

Authentication

Proč se v Django-Tenants: A Real-World Puzzle zlomí přihlášení k subdoméně

Představte si, že vytvoříte aplikaci Django pro více tenantů, kde každá subdoména slouží jinému tenantovi a hladce integruje ověřování uživatelů. Všechno se zdá být perfektní – dokud přihlašovací stránka na subdoméně nevyvolá děsivý problém . Podrbeš se na hlavě a přemýšlíš proč přihlášení funguje bezchybně, ale přihlášení k subdoméně ne. 🤔

Tento problém je frustrující, protože to působí jako paradox: systém jasně rozpoznává uživatele, protože se můžete přihlásit do panelu administrátora. Jakmile se přihlásíte, můžete přistupovat na stránky specifické pro tenanta a dokonce úspěšně odesílat formuláře. Přesto, když se dostanete na přihlašovací stránku, objeví se chyba: Co se vlastně děje pod kapotou?

Dovolte mi podělit se o příbuzný příklad. Je to jako mít dvoje dveře do domu – jedny pro hosty (vaše hlavní doména) a jedny pro rodinu (subdomény). Dveře pro hosty fungují dobře, ale rodinné dveře se zaseknou. Víte, že klíče jsou správné, ale něco hlubšího je špatně s mechanismem zámku – jako neočekávaná neshoda v dotazech na schéma databáze.

Kořen problému spočívá v tom, jak je Django Rest Framework interaguje s knihovna. Konkrétně jsou tokeny dotazovány na místo schématu nájemce, což způsobuje a ForeignKeyViolation chyba. Pojďme se ponořit do tohoto problému, odhalit příčinu a opravit přihlašovací dveře pro všechny vaše subdomény! 🔧

Příkaz Příklad použití
schema_context() Umožňuje přepínání mezi schématy v nastavení Django pro více nájemců. Příklad: with schema_context('tenant_name'): zajišťuje provádění operací v zadaném schématu databáze tenanta.
authenticate() Ověřuje uživatele pomocí jeho přihlašovacích údajů. Příklad: user = authenticate(požadavek, uživatelské jméno=uživatelské jméno, heslo=heslo) zkontroluje, zda jsou zadané přihlašovací údaje platné.
Token.objects.get_or_create() Načte existující token pro uživatele nebo jej vytvoří, pokud neexistuje. Příklad: token, created = Token.objects.get_or_create(user=user).
csrf_exempt Zakáže ochranu CSRF pro konkrétní pohled. Příklad: @csrf_exempt se používá při zpracování externích nebo jiných než prohlížečových požadavků API.
connection.tenant.schema_name Načte název schématu aktuálního tenanta v aplikaci Django pro více tenantů. Příklad: tenant_schema_name = connection.tenant.schema_name.
JsonResponse() Vrátí data ve formátu JSON jako odpověď HTTP. Příklad: return JsonResponse({"status": "úspěch", "token": token.key}).
APIClient() Testovací klient Django Rest Framework, který umožňuje simulaci požadavků HTTP v testech. Příklad: self.client = APIClient().
localStorage.setItem() Uloží pár klíč–hodnota do místního úložiště prohlížeče. Příklad: localStorage.setItem('token', data.token) ukládá token pro budoucí použití.
Swal.fire() Zobrazuje vyskakovací okna pomocí knihovny SweetAlert2. Příklad: Swal.fire({icon: 'error', title: 'Login Failed'}) zobrazuje stylizovanou chybovou zprávu.
TestCase Používá se k psaní jednotkových testů v Django. Příklad: class TenantLoginTest(TestCase): vytvoří testovací třídu pro testování přihlášení specifického pro schéma.

Zvládnutí autentizace specifické pro nájemce v Django-Tenants

Výše uvedené skripty řeší kritický problém v aplikacích Django pro více nájemců, kde jsou tokeny dotazovány z místo příslušného schématu nájemce. K tomuto chování dochází, protože Django Rest Framework (DRF) automaticky nepřepíná schémata při interakci s modely tokenů. Abychom to vyřešili, využíváme knihovny metoda, která nám umožňuje explicitně provádět databázové dotazy v rámci správného schématu tenanta. To zajišťuje, že autentizace uživatele a načítání tokenů budou bezproblémově fungovat pro každého tenanta, ať už k němu přistupujete přes primární doménu nebo subdomény. Bez této úpravy dojde k chybě ForeignKeyViolation, protože systém hledá záznamy uživatelů v nesprávném schématu.

Funkce `dual_login_view` demonstruje, jak ověřovat uživatele a zároveň zajistit, aby databázové připojení ukazovalo na schéma tenanta. Nejprve extrahuje uživatelské jméno a heslo z datové části požadavku. Poté pomocí metody `autenticate` ověří přihlašovací údaje. Pokud bude úspěšný, přihlásí uživatele a vygeneruje token pomocí metody `Token.objects.get_or_create()` DRF. Aby bylo zajištěno, že tento dotaz cílí na správné schéma, funkce `schema_context` zalomí logiku a přepne kontext databáze na schéma aktivního tenanta. To zaručuje, že systém dokáže najít správné záznamy o uživatelích a tokenech, čímž se eliminuje chyba nesouladu schématu.

Třída `TenantAwareLoginAPIView` vylepšuje řešení tím, že využívá Django Rest Framework APIView pro modulární přístup. Přijímá požadavky POST obsahující přihlašovací údaje uživatele, ověřuje je pomocí „autentizace“ a generuje token, pokud jsou přihlašovací údaje správné. Důležité je, že používá `schema_context` k provádění všech operací v rámci správného schématu tenanta. Toto zobrazení založené na třídách je ideální pro moderní implementace API, protože centralizuje zpracování chyb a poskytuje čisté, strukturované odpovědi. Například vrácení tokenu JSON zajistí, že jej frontend může uložit do místního úložiště a použít jej pro následné ověřené požadavky.

Na frontendu hraje skript odeslání formuláře JavaScript klíčovou roli při vytváření bezpečných a strukturovaných požadavků na koncový bod přihlášení. Zabraňuje výchozímu chování formuláře, ověřuje vstupní pole a odesílá pověření spolu s tokenem CSRF prostřednictvím požadavku rozhraní API pro načtení. Po obdržení úspěšné odpovědi se token uloží do `localStorage` a uživatel je přesměrován. Pokud server vrátí chybu, knihovna SweetAlert2 zobrazí přátelskou výstražnou zprávu. Díky tomu je uživatelská zkušenost plynulejší a zajišťuje správnou zpětnou vazbu o chybách. Například při přístupu k subdoméně tenanta by se uživateli přihlášenému s platnými přihlašovacími údaji okamžitě zobrazila zpráva o úspěchu a byl přesměrován na řídicí panel aplikace. 🔒

Řešení problémů s přihlášením k subdoméně v Django-Tenants pomocí optimalizovaných dotazů na schéma

Backendové řešení využívající Django ORM s explicitním výběrem schématu a zpracováním chyb.

# Import necessary libraries
from django.db import connection
from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate, login
from django.http import JsonResponse
from django_tenants.utils import schema_context
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def dual_login_view(request):
    """Handle login for multi-tenant subdomains with correct schema."""
    if request.method == "POST":
        username = request.POST.get("login")
        password = request.POST.get("password")
        tenant_schema_name = connection.tenant.schema_name
        try:
            # Switch to the correct tenant schema
            with schema_context(tenant_schema_name):
                user = authenticate(request, username=username, password=password)
                if user is not None:
                    login(request, user)
                    # Generate or retrieve token
                    token, created = Token.objects.get_or_create(user=user)
                    return JsonResponse({"status": "success", "token": token.key})
                else:
                    return JsonResponse({"status": "error", "message": "Invalid credentials"}, status=400)
        except Exception as e:
            return JsonResponse({"status": "error", "message": str(e)}, status=500)
    return JsonResponse({"status": "error", "message": "Invalid request method"}, status=405)

Explicitní správa tokenů pomocí schémat s vědomím tenantů

Modularizovaný a opakovaně použitelný Django API View pro přihlášení v architektuře s více nájemci.

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth import authenticate
from rest_framework.authtoken.models import Token
from django_tenants.utils import schema_context
class TenantAwareLoginAPIView(APIView):
    """Login endpoint that ensures tenant-aware schema handling."""
    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        tenant_schema_name = request.tenant.schema_name
        if not username or not password:
            return Response({"error": "Username and password required"}, status=status.HTTP_400_BAD_REQUEST)
        try:
            with schema_context(tenant_schema_name):
                user = authenticate(request, username=username, password=password)
                if user is None:
                    return Response({"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
                # Generate or retrieve token for the user
                token, created = Token.objects.get_or_create(user=user)
                return Response({"token": f"Token {token.key}"}, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Frontendový skript pro zpracování požadavků na přihlášení k subdoméně

Řešení JavaScript pro zpracování odesílání formulářů a zpracování přihlašování na základě tokenů pro subdomény tenantů.

<script>
document.querySelector('form').addEventListener('submit', function(event) {
    event.preventDefault();
    let form = event.target;
    let formData = new FormData(form);
    fetch("{% url 'tenant_aware_login' %}", {
        method: 'POST',
        body: JSON.stringify(Object.fromEntries(formData)),
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': formData.get('csrfmiddlewaretoken')
        }
    })
    .then(response => {
        if (!response.ok) throw new Error('Server Error');
        return response.json();
    })
    .then(data => {
        if (data.token) {
            localStorage.setItem('token', data.token);
            window.location.href = '/';
        } else {
            Swal.fire({
                icon: 'error',
                title: 'Login Failed',
                text: data.error || 'Invalid credentials'
            });
        }
    })
    .catch(error => {
        console.error('Error:', error);
    });
});
</script>

Unit Test k ověření Schema-Aware Token Authentication

Test jednotky v Pythonu, aby se zajistilo, že rozhraní API správně zpracovává přepínání schémat.

from django.test import TestCase
from rest_framework.test import APIClient
from django_tenants.utils import schema_context
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
class TenantLoginTest(TestCase):
    def setUp(self):
        self.client = APIClient()
        with schema_context('test_tenant'):  # Switch to tenant schema
            self.user = User.objects.create_user(username='testuser', password='testpass')
    def test_successful_login(self):
        with schema_context('test_tenant'):
            response = self.client.post('/api/login/', {
                'username': 'testuser',
                'password': 'testpass'
            })
            self.assertEqual(response.status_code, 200)
            self.assertIn('token', response.json())
    def test_invalid_login(self):
        with schema_context('test_tenant'):
            response = self.client.post('/api/login/', {
                'username': 'wronguser',
                'password': 'wrongpass'
            })
            self.assertEqual(response.status_code, 401)
            self.assertIn('error', response.json())

Pochopení role tokenových dotazů specifických pro nájemce v aplikacích Django pro více nájemců

Jedním z hlavních aspektů zajišťuje, že databázové operace vždy probíhají v rámci správného schématu tenanta. Problém v tomto případě nastává proto, že výchozí chování Djanga předpokládá jediné sdílené schéma, což vede k chybám, když nelze tokeny nebo uživatele nalézt v . Využitím nástrojů, jako je např funkce z django-nájemníci knihovny, explicitně přepínáme mezi schématy, abychom mohli provádět dotazy specifické pro tenanta. To zajišťuje, že ověřovací dotazy pro uživatele a tokeny jsou směrovány do správného schématu.

Dalším klíčovým detailem, který je často opomíjen, je jak provozuje. Ve výchozím nastavení hledá záznamy uživatelů v aktivním schématu databáze. Pokud je aktuální schéma nesprávné, dotaz selže s a chyba. Abychom to napravili, zajistíme, aby jakýkoli dotaz zahrnující model tokenu proběhl ve správném kontextu schématu tenanta. Bez této úpravy se nepodaří ověřit ani platným uživatelům, protože ID uživatele nelze najít ve výchozím schématu.

Navíc front-endový kód hraje klíčovou roli při efektivní komunikaci s těmito backendovými procesy. Zajištění, že rozhraní fetch API odešle a správné zpracování odpovědí JSON je rozhodující. Například zabalení volání API do bloků try-catch a zpracování chyb pomocí uživatelsky přívětivých knihoven, jako je zlepšuje použitelnost. Tato vylepšení zajišťují, že tok přihlášení zůstane bezproblémový, a to i při přepínání mezi subdoménami nebo při výskytu chyb specifických pro schéma. Představte si například platformu SaaS, kde každá společnost (tenant) používá subdoménu – oprava kontextu schématu zajišťuje, že se každý zaměstnanec přihlásí hladce bez přerušení. 🚀

  1. Co způsobuje a při přihlašování?
  2. K chybě dochází, protože dotazuje nesprávné schéma, což způsobí nesoulad při vyhledávání uživatelských záznamů.
  3. Jak zajistím, aby dotazy na token odkazovaly na správné schéma tenanta?
  4. Použití z knihovnu, aby zalomila provádění dotazu a přepnula na správné schéma.
  5. Proč funguje přihlášení do panelu administrátora, ale přihlášení uživatele se nezdaří?
  6. Správce Django automaticky upraví kontexty schématu, ale pomocí vlastních pohledů nebo nemusí, pokud to není výslovně nakonfigurováno.
  7. Jak získám a uložím přihlašovací token na frontendu?
  8. Pomocí rozhraní fetch API odešlete pověření a poté uložte token odpovědi pomocí pro trvalé ověřování.
  9. Jak mohu zobrazit lepší chybové zprávy pro neúspěšné přihlášení?
  10. Implementujte frontendová upozornění pomocí knihoven jako k upozornění uživatelů na nesprávné přihlašovací údaje nebo problémy se serverem.

Řešení selhání přihlášení v aplikacích pro více tenantů Django vyžaduje zajistit, aby všechny databázové dotazy fungovaly ve správném schématu. Explicitním použitím nástrojů, jako je kontext schématu, můžeme zaručit, že uživatelské tokeny budou načteny ze správné databáze tenantů, čímž se zabrání konfliktům schémat.

Představte si, že pracujete na platformě SaaS, kde uživatelé čelí selhání přihlášení pouze na subdoménách. Při správném přepínání schémat jsou tyto problémy vyřešeny a zajišťuje bezproblémové ověřování. Přijetí této opravy nejen zlepšuje ale také zaručuje bezpečný a efektivní přístup k datům pro každého nájemce. 🔧

  1. Podrobná dokumentace na knihovna, vysvětlující správu schémat v aplikacích pro více nájemců. Dostupné na: Dokumentace Django-Tenants .
  2. Oficiální dokumentace Django Rest Framework (DRF) o ověřování tokenů. Více se dozvíte na: Ověření tokenu DRF .
  3. Komplexní průvodce používáním schema_context v prostředích s více klienty. Nalezeno na: GitHub – nájemci Django .
  4. Statistiky o manipulaci s tokeny CSRF v aplikacích Django: Dokumentace Django CSRF .
  5. Osvědčené postupy pro navrhování platforem SaaS pro více tenantů, včetně ověřování uživatelů: SaaS Pegasus Multi-Tenancy průvodce .