Django-Tenants: 실제 퍼즐에서 하위 도메인 로그인이 중단되는 이유
모든 하위 도메인이 서로 다른 테넌트를 서비스하고 사용자 인증을 원활하게 통합하는 다중 테넌트 Django 애플리케이션을 구축한다고 상상해 보세요. 모든 것이 완벽해 보입니다. 하위 도메인의 로그인 페이지에서 두려운 오류가 발생하기 전까지는 말이죠. 500 내부 서버 오류. 당신은 머리를 긁적이며 왜 그랬는지 궁금해합니다. 기본 도메인 로그인은 완벽하게 작동하지만 하위 도메인 로그인은 그렇지 않습니다. 🤔
이 문제는 역설처럼 느껴지기 때문에 실망스럽습니다. 관리자 패널에 로그인할 수 있기 때문에 시스템이 사용자를 명확하게 인식합니다. 로그인하면 테넌트별 페이지에 액세스하고 양식을 성공적으로 제출할 수도 있습니다. 그러나 로그인 페이지를 누르면 오류가 나타납니다. "예기치 않은 토큰' 실제로 내부에서 무슨 일이 일어나고 있나요?
관련 있는 예를 하나 공유하겠습니다. 이는 집에 두 개의 문이 있는 것과 같습니다. 하나는 손님용(기본 도메인)이고 다른 하나는 가족용(하위 도메인)입니다. 손님용 문은 잘 작동하지만 가족용 문은 막힙니다. 키가 정확하다는 것을 알고 있지만 잠금 메커니즘에는 데이터베이스 스키마 쿼리의 예상치 못한 불일치와 같은 더 심각한 문제가 있습니다.
문제의 근본 원인은 Django Rest Framework의 토큰 인증 와 상호작용한다 django-세입자 도서관. 특히 토큰은 다음에 대해 쿼리됩니다. 공개 스키마 테넌트 스키마 대신 ForeignKey 위반 오류. 이 문제를 자세히 조사하고 원인을 찾아내고 모든 하위 도메인의 로그인 도어를 수정해 봅시다! 🔧
명령 | 사용예 |
---|---|
schema_context() | 다중 테넌트 Django 설정에서 스키마 간 전환을 허용합니다. 예: Schema_context('tenant_name') 사용: 지정된 테넌트의 데이터베이스 스키마에서 작업이 실행되도록 합니다. |
authenticate() | 자격 증명을 사용하여 사용자를 인증합니다. 예: user = authenticate(request, 사용자 이름=사용자 이름, 비밀번호=password)는 제공된 자격 증명이 유효한지 확인합니다. |
Token.objects.get_or_create() | 사용자의 기존 토큰을 검색하거나 존재하지 않는 경우 토큰을 생성합니다. 예: 토큰, 생성됨 = Token.objects.get_or_create(user=user). |
csrf_exempt | 특정 보기에 대한 CSRF 보호를 비활성화합니다. 예: @csrf_exempt는 외부 또는 브라우저가 아닌 API 요청을 처리할 때 사용됩니다. |
connection.tenant.schema_name | Django 다중 테넌트 앱에서 현재 테넌트의 스키마 이름을 검색합니다. 예: 테넌트_스키마_이름 = 연결.테넌트.스키마_이름. |
JsonResponse() | JSON 형식의 데이터를 HTTP 응답으로 반환합니다. 예: JsonResponse({"status": "success", "token": token.key})를 반환합니다. |
APIClient() | 테스트에서 HTTP 요청을 시뮬레이션할 수 있는 Django Rest Framework 테스트 클라이언트입니다. 예: self.client = APIClient(). |
localStorage.setItem() | 브라우저의 로컬 저장소에 키-값 쌍을 저장합니다. 예: localStorage.setItem('token', data.token)은 나중에 사용할 수 있도록 토큰을 저장합니다. |
Swal.fire() | SweetAlert2 라이브러리를 사용하여 경고 팝업을 표시합니다. 예: Swal.fire({icon: 'error', title: 'Login Failed'}) 는 스타일이 지정된 오류 메시지를 표시합니다. |
TestCase | Django에서 단위 테스트를 작성하는 데 사용됩니다. 예: class TenantLoginTest(TestCase): 스키마별 로그인 테스트를 위한 테스트 클래스를 생성합니다. |
Django-Tenants에서 테넌트별 인증 마스터하기
위에 제공된 스크립트는 토큰이 쿼리되는 다중 테넌트 Django 애플리케이션의 중요한 문제를 해결합니다. 공개 스키마 적절한 테넌트 스키마 대신. 이 동작은 DRF(Django Rest Framework)가 토큰 모델과 상호 작용할 때 자동으로 스키마를 전환하지 않기 때문에 발생합니다. 이를 해결하기 위해 우리는 django-세입자 도서관의 스키마_컨텍스트 메서드를 사용하면 올바른 테넌트의 스키마 내에서 데이터베이스 쿼리를 명시적으로 실행할 수 있습니다. 이렇게 하면 기본 도메인을 통해 액세스하든 하위 도메인을 통해 액세스하든 관계없이 각 테넌트에 대해 사용자 인증 및 토큰 검색이 원활하게 작동합니다. 이 조정이 없으면 시스템이 잘못된 스키마에서 사용자 레코드를 찾기 때문에 ForeignKeyViolation 오류가 발생합니다.
`dual_login_view` 함수는 데이터베이스 연결이 테넌트 스키마를 가리키는지 확인하면서 사용자를 인증하는 방법을 보여줍니다. 먼저 요청 페이로드에서 사용자 이름과 비밀번호를 추출합니다. 그런 다음 'authenticate' 메서드를 사용하여 자격 증명의 유효성을 검사합니다. 성공하면 사용자를 로그인하고 DRF의 'Token.objects.get_or_create()' 메서드를 사용하여 토큰을 생성합니다. 이 쿼리가 올바른 스키마를 대상으로 하는지 확인하기 위해 `schema_context` 함수는 논리를 래핑하여 데이터베이스 컨텍스트를 활성 테넌트 스키마로 전환합니다. 이는 시스템이 올바른 사용자 및 토큰 레코드를 찾을 수 있도록 보장하여 스키마 불일치 오류를 제거합니다.
'TenantAwareLoginAPIView' 클래스는 모듈식 접근 방식을 위해 Django Rest Framework의 APIView를 채택하여 솔루션을 향상시킵니다. 사용자 자격 증명이 포함된 POST 요청을 수락하고, `authenticate`를 사용하여 유효성을 검사하고, 자격 증명이 올바른 경우 토큰을 생성합니다. 중요한 것은 `schema_context`를 사용하여 올바른 테넌트 스키마 내에서 모든 작업을 실행한다는 것입니다. 이 클래스 기반 보기는 오류 처리를 중앙 집중화하고 깔끔하고 구조화된 응답을 제공하므로 최신 API 구현에 이상적입니다. 예를 들어 JSON 토큰을 반환하면 프런트엔드가 이를 로컬 저장소에 저장하고 후속 인증 요청에 사용할 수 있습니다.
프런트엔드에서 JavaScript 양식 제출 스크립트는 로그인 엔드포인트에 안전하고 구조화된 요청을 보내는 데 중요한 역할을 합니다. 기본 양식 동작을 방지하고, 입력 필드의 유효성을 검사하고, 가져오기 API 요청을 통해 CSRF 토큰과 함께 자격 증명을 보냅니다. 성공적인 응답을 받으면 토큰이 `localStorage`에 저장되고 사용자가 리디렉션됩니다. 서버가 오류를 반환하면 SweetAlert2 라이브러리는 친숙한 경고 메시지를 표시합니다. 이를 통해 사용자 경험이 더욱 원활해지고 적절한 오류 피드백이 보장됩니다. 예를 들어, 테넌트 하위 도메인에 액세스할 때 유효한 자격 증명으로 로그인하는 사용자는 즉시 성공 메시지를 확인하고 애플리케이션 대시보드로 리디렉션됩니다. 🔒
최적화된 스키마 쿼리를 사용하여 Django-Tenants의 하위 도메인 로그인 문제 처리
명시적인 스키마 선택 및 오류 처리 기능을 갖춘 Django ORM을 사용하는 백엔드 솔루션입니다.
# 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)
테넌트 인식 스키마를 사용한 명시적 토큰 관리
다중 테넌트 아키텍처의 로그인을 위한 모듈화되고 재사용 가능한 Django API 보기입니다.
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)
하위 도메인 로그인 요청 처리를 위한 프런트엔드 스크립트
양식 제출을 처리하고 테넌트 하위 도메인에 대한 토큰 기반 로그인을 처리하는 JavaScript 솔루션입니다.
<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>
스키마 인식 토큰 인증을 확인하기 위한 단위 테스트
API가 스키마 전환을 올바르게 처리하는지 확인하기 위한 Python의 단위 테스트입니다.
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())
다중 테넌트 Django 앱에서 테넌트별 토큰 쿼리의 역할 이해
한 가지 주요 측면 다중 테넌트 Django 앱 데이터베이스 작업이 항상 올바른 테넌트 스키마 내에서 발생하도록 보장합니다. 이 경우 문제는 Django의 기본 동작이 단일 공유 스키마를 가정하여 토큰이나 사용자를 찾을 수 없을 때 오류가 발생하기 때문에 발생합니다. 공개 스키마. 다음과 같은 도구를 활용하여 schema_context 에서 기능 django-세입자 라이브러리에서는 스키마 간에 명시적으로 전환하여 테넌트별 쿼리를 수행합니다. 이렇게 하면 사용자 및 토큰에 대한 인증 쿼리가 올바른 스키마로 전달됩니다.
종종 간과되는 또 다른 주요 세부 사항은 다음과 같습니다. Token.objects.get_or_create() 운영합니다. 기본적으로 활성 데이터베이스 스키마에서 사용자 레코드를 찾습니다. 현재 스키마가 올바르지 않으면 다음과 같은 쿼리가 실패합니다. ForeignKey 위반 오류. 이 문제를 해결하기 위해 토큰 모델과 관련된 모든 쿼리가 적절한 테넌트 스키마 컨텍스트 내에서 발생하는지 확인합니다. 이 조정이 없으면 유효한 사용자라도 기본 스키마에서 사용자 ID를 찾을 수 없기 때문에 인증에 실패하게 됩니다.
또한 프런트엔드 코드는 이러한 백엔드 프로세스와 효과적으로 통신하는 데 중요한 역할을 합니다. 가져오기 API가 다음을 전송하는지 확인합니다. CSRF 토큰 JSON 응답을 올바르게 처리하는 것이 중요합니다. 예를 들어 try-catch 블록으로 API 호출을 래핑하고 다음과 같은 사용자 친화적인 라이브러리를 사용하여 오류를 처리합니다. SweetAlert2 유용성을 향상시킵니다. 이러한 향상된 기능을 통해 하위 도메인 간에 전환하거나 스키마별 오류가 발생하는 경우에도 로그인 흐름이 원활하게 유지됩니다. 예를 들어, 모든 회사(테넌트)가 하위 도메인을 사용하는 SaaS 플랫폼을 상상해 보세요. 스키마 컨텍스트를 수정하면 모든 직원이 중단 없이 원활하게 로그인할 수 있습니다. 🚀
다중 테넌트 Django 로그인 문제에 대한 일반적인 질문
- 원인은 무엇입니까? 500 내부 서버 오류 로그인 중에?
- 오류가 발생하는 이유는 Token.objects.get_or_create() 잘못된 스키마를 쿼리하여 사용자 레코드를 조회할 때 불일치가 발생합니다.
- 토큰 쿼리가 올바른 테넌트 스키마를 가리키는지 어떻게 확인하나요?
- 사용 schema_context() 에서 django-세입자 쿼리 실행을 래핑하고 올바른 스키마로 전환하는 라이브러리입니다.
- 관리자 패널 로그인은 작동하지만 사용자 로그인은 실패하는 이유는 무엇입니까?
- Django 관리자는 스키마 컨텍스트를 자동으로 조정하지만 사용자 정의 보기는 authenticate() 또는 Token.objects 명시적으로 구성하지 않는 한 그렇지 않을 수 있습니다.
- 프런트엔드에서 로그인 토큰을 어떻게 검색하고 저장합니까?
- 가져오기 API를 사용하여 자격 증명을 보낸 다음 다음을 사용하여 응답 토큰을 저장합니다. localStorage.setItem() 지속적인 인증을 위해.
- 로그인 실패에 대한 오류 메시지를 더 잘 표시하려면 어떻게 해야 합니까?
- 다음과 같은 라이브러리를 사용하여 프런트엔드 알림을 구현합니다. SweetAlert2 사용자에게 잘못된 자격 증명이나 서버 문제를 알리기 위해.
테넌트 하위 도메인 전체에서 원활한 로그인 보장
Django 다중 테넌트 앱에서 로그인 실패를 해결하려면 모든 데이터베이스 쿼리가 적절한 스키마에서 작동하는지 확인해야 합니다. 스키마 컨텍스트와 같은 도구를 명시적으로 사용하면 스키마 충돌을 방지하면서 올바른 테넌트 데이터베이스에서 사용자 토큰을 가져오도록 보장할 수 있습니다.
사용자가 하위 도메인에서만 로그인 실패에 직면하는 SaaS 플랫폼에서 작업한다고 상상해 보십시오. 적절한 스키마 전환을 통해 이러한 문제가 해결되어 원활한 인증이 보장됩니다. 이 수정 사항을 채택하면 개선될 뿐만 아니라 사용자 경험 또한 각 테넌트에 대해 안전하고 효율적인 데이터 액세스를 보장합니다. 🔧
Django-Tenant 하위 도메인 문제를 이해하기 위한 소스 및 참조
- 에 대한 자세한 문서 django-세입자 다중 테넌트 애플리케이션의 스키마 관리를 설명하는 라이브러리입니다. 이용 가능 장소: Django-테넌트 문서 .
- 토큰 인증에 대한 공식 DRF(Django Rest Framework) 문서입니다. 다음에서 자세히 알아보세요. DRF 토큰 인증 .
- 다중 테넌트 환경에서 Schema_context 사용에 대한 종합 가이드입니다. 다음에서 발견: GitHub - Django 테넌트 .
- Django 애플리케이션에서 CSRF 토큰 처리에 대한 통찰력: 장고 CSRF 문서 .
- 사용자 인증을 포함한 다중 테넌트 SaaS 플랫폼 설계 모범 사례: SaaS Pegasus 다중 테넌시 가이드 .