Lösning av Django-Tenant-underdomäninloggningsfel med Rest Framework-tokens

Authentication

Varför underdomäninloggningar går sönder i Django-Tenants: A Real-World Puzzle

Föreställ dig att bygga en Django-applikation med flera hyresgäster där varje underdomän betjänar en annan hyresgäst och sömlöst integrerar användarautentisering. Allt verkar perfekt - tills inloggningssidan på en underdomän kastar en fruktad . Du kliar dig i huvudet och undrar varför inloggning fungerar felfritt, men underdomäninloggningen gör det inte. 🤔

Det här problemet är frustrerande eftersom det känns som en paradox: systemet känner tydligt igen användare eftersom du kan logga in på adminpanelen. När du väl är inloggad kan du komma åt hyresgästspecifika sidor och till och med skicka in formulär. Men när du trycker på inloggningssidan dyker ett fel upp: Vad händer under huven egentligen?

Låt mig dela ett relaterbart exempel. Det är som att ha två dörrar till ett hus – en för gäster (din huvuddomän) och en för familj (underdomäner). Gästdörren fungerar bra, men familjens dörr fastnar. Du vet att nycklarna är korrekta, men något djupare är fel med låsmekanismen – som en oväntad oöverensstämmelse i databasschemafrågor.

Roten till problemet ligger i hur Django Rest Framework fungerar interagerar med bibliotek. Specifikt frågas tokens mot istället för hyresgästschemat, vilket orsakar en ForeignKeyViolation fel. Låt oss dyka in i det här problemet, avslöja orsaken och fixa inloggningsdörren för alla dina underdomäner! 🔧

Kommando Exempel på användning
schema_context() Tillåter växling mellan scheman i en Django-konfiguration med flera klienter. Exempel: with schema_context('tenant_name'): säkerställer att operationer exekveras i den angivna klientens databasschema.
authenticate() Autentiserar en användare med deras autentiseringsuppgifter. Exempel: användare = autentisera (begäran, användarnamn=användarnamn, lösenord=lösenord) kontrollerar om de angivna referenserna är giltiga.
Token.objects.get_or_create() Hämtar en befintlig token för en användare eller skapar en om den inte finns. Exempel: token, created = Token.objects.get_or_create(användare=användare).
csrf_exempt Inaktiverar CSRF-skydd för en specifik vy. Exempel: @csrf_exempt används vid hantering av externa eller icke-webbläsare API-förfrågningar.
connection.tenant.schema_name Hämtar den aktuella hyresgästens schemanamn i en Django multi-tenant-app. Exempel: tenant_schema_name = anslutning.hyresgäst.schema_name.
JsonResponse() Returnerar JSON-formaterad data som ett HTTP-svar. Exempel: return JsonResponse({"status": "framgång", "token": token.key}).
APIClient() En Django Rest Framework-testklient som tillåter simulering av HTTP-förfrågningar i tester. Exempel: self.client = APIClient().
localStorage.setItem() Sparar ett nyckel-värdepar i webbläsarens lokala lagring. Exempel: localStorage.setItem('token', data.token) lagrar token för framtida användning.
Swal.fire() Visar popup-fönster för varningar med SweetAlert2-biblioteket. Exempel: Swal.fire({icon: 'error', title: 'Login Failed'}) visar ett formaterat felmeddelande.
TestCase Används för att skriva enhetstester i Django. Exempel: class TenantLoginTest(TestCase): skapar en testklass för schemaspecifik inloggningstestning.

Bemästra Tenant-Specific Authentication i Django-Tenants

Skripten som tillhandahålls ovan tar itu med ett kritiskt problem i Django-applikationer med flera innehavare där tokens efterfrågas från istället för lämpligt hyresgästschema. Det här beteendet beror på att Django Rest Framework (DRF) inte automatiskt byter schema när de interagerar med tokenmodeller. För att lösa detta utnyttjar vi bibliotekets metod, vilket gör att vi explicit kan köra databasfrågor inom rätt hyresgästs schema. Detta säkerställer att användarverifiering och tokenhämtning fungerar sömlöst för varje klient, oavsett om den nås via den primära domänen eller underdomänerna. Utan denna justering uppstår ForeignKeyViolation-felet eftersom systemet söker efter användarposter i fel schema.

Funktionen `dual_login_view` visar hur man autentiserar användare samtidigt som man säkerställer att databasanslutningen pekar på klientschemat. Först extraherar den användarnamnet och lösenordet från förfrågans nyttolast. Sedan, med hjälp av `autenticate`-metoden, validerar den autentiseringsuppgifterna. Om det lyckas loggar den in användaren och genererar en token med DRF:s `Token.objects.get_or_create()`-metod. För att säkerställa att den här frågan riktar sig till rätt schema, omsluter funktionen "schema_kontext" logiken och byter databaskontext till det aktiva klientschemat. Detta garanterar att systemet kan hitta rätt användar- och tokenposter, vilket eliminerar schemafelet.

Klassen `TenantAwareLoginAPIView` förbättrar lösningen genom att använda Django Rest Frameworks APIView för ett modulärt tillvägagångssätt. Den accepterar POST-förfrågningar som innehåller användaruppgifterna, validerar dem med hjälp av `autenticate` och genererar en token om referenserna är korrekta. Viktigt är att den använder "schema_context" för att utföra alla operationer inom rätt klientschema. Denna klassbaserade vy är idealisk för moderna API-implementeringar eftersom den centraliserar felhantering och ger rena, strukturerade svar. Att till exempel returnera en JSON-token säkerställer att gränssnittet kan lagra den i lokal lagring och använda den för efterföljande autentiserade förfrågningar.

I gränssnittet spelar JavaScript-formulärinlämningsskriptet en nyckelroll för att göra säkra och strukturerade förfrågningar till inloggningsslutpunkten. Det förhindrar standardformulärbeteendet, validerar inmatningsfält och skickar autentiseringsuppgifterna tillsammans med CSRF-token via en hämta API-begäran. När ett framgångsrikt svar har mottagits lagras token i `localStorage` och användaren omdirigeras. Om servern returnerar ett fel, visar SweetAlert2-biblioteket ett vänligt varningsmeddelande. Detta gör användarupplevelsen smidigare och säkerställer korrekt felåterkoppling. Till exempel, när en användare som loggar in med giltiga uppgifter omedelbart får tillgång till en underdomän för en hyresgäst, ser han ett framgångsmeddelande och omdirigeras till applikationens instrumentpanel. 🔒

Hantera subdomäninloggningsproblem hos Django-Tenants med optimerade schemafrågor

Backend-lösning med Django ORM med explicit schemaval och felhantering.

# 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)

Explicit Token Management med Tenant-Aware Schemas

En modulariserad och återanvändbar Django API View för inloggning i en arkitektur med flera hyresgäster.

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)

Frontend-skript för hantering av subdomäninloggningsförfrågningar

JavaScript-lösning för att hantera formulärinlämning och processtokenbaserad inloggning för hyresgästunderdomäner.

<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>

Enhetstest för att verifiera Schema-Aware Token Authentication

Enhetstest i Python för att säkerställa att API hanterar schemaväxling korrekt.

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())

Förstå rollen för hyresgästspecifika tokenfrågor i Django-appar för flera hyresgäster

En viktig aspekt av ser till att databasoperationer alltid sker inom rätt klientschema. Problemet i det här fallet beror på att Djangos standardbeteende antar ett enda delat schema, vilket leder till fel när tokens eller användare inte kan hittas i . Genom att utnyttja verktyg som funktion från django-hyresgäster bibliotek, växlar vi uttryckligen mellan scheman för att utföra hyresgästspecifika frågor. Detta säkerställer att autentiseringsfrågor för användare och tokens riktas till rätt schema.

En annan viktig detalj som ofta förbises är hur fungerar. Som standard letar den efter användarposter i det aktiva databasschemat. Om det aktuella schemat är felaktigt misslyckas frågan med a fel. För att åtgärda detta ser vi till att alla frågor som involverar tokenmodellen sker inom ett korrekt klientschemakontext. Utan denna justering kommer även giltiga användare att misslyckas med att autentisera eftersom användarens ID inte kan lokaliseras i standardschemat.

Dessutom spelar front-end-kod en avgörande roll för att effektivt kommunicera med dessa backend-processer. Se till att hämta API skickar och korrekt hanterar JSON-svar är avgörande. Till exempel att slå in API-anrop i try-catch-block och hantera fel med hjälp av användarvänliga bibliotek som förbättrar användbarheten. Dessa förbättringar säkerställer att inloggningsflödet förblir sömlöst, även när du byter mellan underdomäner eller stöter på schemaspecifika fel. Föreställ dig till exempel en SaaS-plattform där varje företag (hyresgäst) använder en underdomän – fixande av schemakontext säkerställer att varje anställd loggar in smidigt utan störningar. 🚀

  1. Vad orsakar a under inloggning?
  2. Felet uppstår pga frågar efter fel schema, vilket orsakar en oöverensstämmelse när man letar upp användarposter.
  3. Hur säkerställer jag att tokenfrågor pekar på rätt klientschema?
  4. Använda från biblioteket för att avsluta frågekörningen och byta till rätt schema.
  5. Varför fungerar adminpanelens inloggning men användarinloggningen misslyckas?
  6. Django-administratören justerar automatiskt schemakontexter, men anpassade vyer använder eller kanske inte om det inte är uttryckligen konfigurerat.
  7. Hur hämtar och lagrar jag en inloggningstoken på frontend?
  8. Använd hämtnings-API:et för att skicka autentiseringsuppgifter och lagra sedan svarstoken med hjälp av för beständig autentisering.
  9. Hur kan jag visa bättre felmeddelanden för misslyckade inloggningar?
  10. Implementera frontend-varningar med hjälp av bibliotek som för att meddela användare om felaktiga referenser eller serverproblem.

För att lösa inloggningsfel i Django multi-tenant-appar måste du säkerställa att alla databasfrågor fungerar i rätt schema. Genom att uttryckligen använda verktyg som schemakontext kan vi garantera att användartokens hämtas från rätt klientdatabas, vilket undviker schemakonflikter.

Föreställ dig att arbeta på en SaaS-plattform där användare får inloggningsfel endast på underdomäner. Med korrekt schemaväxling löses dessa problem, vilket säkerställer sömlös autentisering. Att anta denna fix förbättrar inte bara men garanterar också säker och effektiv dataåtkomst för varje hyresgäst. 🔧

  1. Detaljerad dokumentation om bibliotek, som förklarar schemahantering i applikationer med flera klienter. Tillgänglig på: Django-Tenants Dokumentation .
  2. Officiell Django Rest Framework (DRF) dokumentation om token-autentisering. Läs mer på: DRF-tokenautentisering .
  3. Omfattande guide om hur du använder schema_context i multi-tenant-miljöer. Hittade på: GitHub - Django-hyresgäster .
  4. Insikter om hantering av CSRF-tokens i Django-applikationer: Django CSRF-dokumentation .
  5. Bästa metoder för att designa SaaS-plattformar för flera klienter, inklusive användarautentisering: SaaS Pegasus Multi-Tenancy Guide .