Django REST Framework (DRF) を使用して電話番号検証システムを実装するには、次の手順に従います。このシステムにより、ユーザーは自分の電話番号を入力し、SMS (Twilio など) で確認コードを受け取り、このコードを検証して自分の番号を確認することができます。
まず、必要なライブラリがインストールされていることを確認してください:
pip 経由でインストールします:
pip install djangorestframework twilio django-phonenumber-field
settings.pyのINSTALLED_APPSにphonenumber_fieldとrest_frameworkを追加します:
# settings.py INSTALLED_APPS = [ # ... 'rest_framework', 'phonenumber_field', # ... ]
カスタム ユーザー テンプレートを使用している場合は、電話番号と確認フラグのフィールドを追加します。
# models.py from django.contrib.auth.models import AbstractBaseUser, BaseUserManager from django.db import models from phonenumber_field.modelfields import PhoneNumberField class UserManager(BaseUserManager): def create_user(self, email, username, phone_number, password=None): if not email: raise ValueError('Les utilisateurs doivent avoir une adresse email') if not phone_number: raise ValueError('Les utilisateurs doivent avoir un numéro de téléphone') user = self.model( email=self.normalize_email(email), username=username, phone_number=phone_number, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, username, phone_number, password=None): user = self.create_user( email, username, phone_number, password=password, ) user.is_admin = True user.save(using=self._db) return user class CustomUser(AbstractBaseUser): email = models.EmailField(verbose_name='adresse email', max_length=255, unique=True) username = models.CharField(max_length=50, unique=True) phone_number = PhoneNumberField(unique=True, null=False, blank=False) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) is_phone_verified = models.BooleanField(default=False) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username', 'phone_number'] def __str__(self): return self.email @property def is_staff(self): return self.is_admin
注: すでにユーザー モデルがある場合は、phone_number フィールドと is_phone_verified フィールドを適切に追加してください。
このテンプレートには、ユーザーに送信される確認コードが保存されます。
# models.py import random import string from django.utils import timezone from datetime import timedelta class PhoneVerification(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='phone_verifications') code = models.CharField(max_length=6) created_at = models.DateTimeField(auto_now_add=True) is_verified = models.BooleanField(default=False) def is_expired(self): return self.created_at < timezone.now() - timedelta(minutes=10) # Expire après 10 minutes def __str__(self): return f"Vérification de {self.user.email} - {'Validé' if self.is_verified else 'En attente'}"
Twilio を使用してテキスト メッセージを送信できます。まず、Twilio アカウントを作成し、必要な認証情報 (ACCOUNT_SID、AUTH_TOKEN、FROM_NUMBER) を取得します。
これらの設定を settings.py に追加します:
# settings.py TWILIO_ACCOUNT_SID = 'votre_account_sid' TWILIO_AUTH_TOKEN = 'votre_auth_token' TWILIO_FROM_NUMBER = '+1234567890' # Numéro Twilio
SMS の送信を管理するための utils.py ファイルを作成します:
# utils.py from django.conf import settings from twilio.rest import Client def send_sms(to, message): client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN) message = client.messages.create( body=message, from_=settings.TWILIO_FROM_NUMBER, to=str(to) ) return message.sid
検証リクエストとコード検証を処理するシリアライザーを作成します。
pip install djangorestframework twilio django-phonenumber-field
検証リクエストとコード検証を管理するためのビューを作成します。
# settings.py INSTALLED_APPS = [ # ... 'rest_framework', 'phonenumber_field', # ... ]
注: 検証中にユーザーを作成する場合や、既存のユーザーを別の方法で管理する場合など、必要に応じてこれらのビューを調整できます。
対応するルートを urls.py に追加します。
# models.py from django.contrib.auth.models import AbstractBaseUser, BaseUserManager from django.db import models from phonenumber_field.modelfields import PhoneNumberField class UserManager(BaseUserManager): def create_user(self, email, username, phone_number, password=None): if not email: raise ValueError('Les utilisateurs doivent avoir une adresse email') if not phone_number: raise ValueError('Les utilisateurs doivent avoir un numéro de téléphone') user = self.model( email=self.normalize_email(email), username=username, phone_number=phone_number, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, username, phone_number, password=None): user = self.create_user( email, username, phone_number, password=password, ) user.is_admin = True user.save(using=self._db) return user class CustomUser(AbstractBaseUser): email = models.EmailField(verbose_name='adresse email', max_length=255, unique=True) username = models.CharField(max_length=50, unique=True) phone_number = PhoneNumberField(unique=True, null=False, blank=False) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) is_phone_verified = models.BooleanField(default=False) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username', 'phone_number'] def __str__(self): return self.email @property def is_staff(self): return self.is_admin
リクエスト ビューを編集して、コードを特定のユーザーに関連付けるか、新しいユーザーを作成します。
悪用を避けるために、ユーザーまたは電話番号ごとの確認リクエストの数を制限します。
# models.py import random import string from django.utils import timezone from datetime import timedelta class PhoneVerification(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='phone_verifications') code = models.CharField(max_length=6) created_at = models.DateTimeField(auto_now_add=True) is_verified = models.BooleanField(default=False) def is_expired(self): return self.created_at < timezone.now() - timedelta(minutes=10) # Expire après 10 minutes def __str__(self): return f"Vérification de {self.user.email} - {'Validé' if self.is_verified else 'En attente'}"
検証後にユーザーを作成するか、番号を既存のユーザーに関連付けるかを決定できます。
システムを運用環境にデプロイする前に、必ず開発環境でシステムをテストしてください。以下を確認してください:
概要を説明するために、影響を受けるファイルの完全な例を次に示します。
# settings.py TWILIO_ACCOUNT_SID = 'votre_account_sid' TWILIO_AUTH_TOKEN = 'votre_auth_token' TWILIO_FROM_NUMBER = '+1234567890' # Numéro Twilio
# utils.py from django.conf import settings from twilio.rest import Client def send_sms(to, message): client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN) message = client.messages.create( body=message, from_=settings.TWILIO_FROM_NUMBER, to=str(to) ) return message.sid
# serializers.py from rest_framework import serializers from .models import CustomUser, PhoneVerification from phonenumber_field.serializerfields import PhoneNumberField class PhoneVerificationRequestSerializer(serializers.Serializer): phone_number = PhoneNumberField() def validate_phone_number(self, value): if CustomUser.objects.filter(phone_number=value).exists(): raise serializers.ValidationError("Ce numéro de téléphone est déjà utilisé.") return value class PhoneVerificationCodeSerializer(serializers.Serializer): phone_number = PhoneNumberField() code = serializers.CharField(max_length=6) def validate(self, data): phone_number = data.get('phone_number') code = data.get('code') try: user = CustomUser.objects.get(phone_number=phone_number) except CustomUser.DoesNotExist: raise serializers.ValidationError("Utilisateur non trouvé avec ce numéro de téléphone.") try: verification = PhoneVerification.objects.filter(user=user, code=code, is_verified=False).latest('created_at') except PhoneVerification.DoesNotExist: raise serializers.ValidationError("Code de vérification invalide.") if verification.is_expired(): raise serializers.ValidationError("Le code de vérification a expiré.") data['user'] = user data['verification'] = verification return data
# views.py from rest_framework import generics, status from rest_framework.response import Response from .serializers import PhoneVerificationRequestSerializer, PhoneVerificationCodeSerializer from .models import CustomUser, PhoneVerification from .utils import send_sms import random import string from django.utils import timezone from rest_framework.permissions import AllowAny class PhoneVerificationRequestView(generics.GenericAPIView): serializer_class = PhoneVerificationRequestSerializer permission_classes = [AllowAny] def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) phone_number = serializer.validated_data['phone_number'] # Générer un code de 6 chiffres code = ''.join(random.choices(string.digits, k=6)) try: user = CustomUser.objects.get(phone_number=phone_number) # Si l'utilisateur existe déjà, ne pas permettre la création d'un nouveau return Response({"detail": "Ce numéro de téléphone est déjà associé à un utilisateur."}, status=status.HTTP_400_BAD_REQUEST) except CustomUser.DoesNotExist: pass # Permettre la création si nécessaire # Créer une instance de PhoneVerification verification = PhoneVerification.objects.create(user=None, code=code) # user=None pour l'instant # Envoyer le code par SMS try: send_sms(phone_number, f"Votre code de vérification est : {code}") except Exception as e: return Response({"detail": "Erreur lors de l'envoi du SMS."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response({"detail": "Code de vérification envoyé."}, status=status.HTTP_200_OK) class PhoneVerificationCodeView(generics.GenericAPIView): serializer_class = PhoneVerificationCodeSerializer permission_classes = [AllowAny] def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.validated_data['user'] verification = serializer.validated_data['verification'] # Marquer la vérification comme validée verification.is_verified = True verification.save() # Mettre à jour l'utilisateur user.is_phone_verified = True user.save() return Response({"detail": "Numéro de téléphone vérifié avec succès."}, status=status.HTTP_200_OK)
# urls.py from django.urls import path from .views import PhoneVerificationRequestView, PhoneVerificationCodeView urlpatterns = [ path('api/verify-phone/request/', PhoneVerificationRequestView.as_view(), name='phone-verification-request'), path('api/verify-phone/verify/', PhoneVerificationCodeView.as_view(), name='phone-verification-verify'), ]
検証の試行を制限する: ブルート フォース攻撃を回避するために、検証の試行回数を制限するシステムを実装します。
コードの暗号化: セキュリティを強化するために、データベース内の確認コードを暗号化できます。
非同期タスクを使用する: パフォーマンスを向上させるには、非同期タスク (Celery など) を使用して、API リクエストをブロックせずに SMS を送信します。
HTTPS の構成: 通信を保護するために、API が HTTPS 経由でアクセスできることを確認してください。
以上がDRF プロジェクトでの電話番号検証の実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。