ホームページ > バックエンド開発 > Python チュートリアル > DRF プロジェクトでの電話番号検証の実装

DRF プロジェクトでの電話番号検証の実装

Mary-Kate Olsen
リリース: 2024-12-28 10:17:10
オリジナル
142 人が閲覧しました

Implémentation de vérification de numéro de téléphone dans un projet drf

Django REST Framework (DRF) を使用して電話番号検証システムを実装するには、次の手順に従います。このシステムにより、ユーザーは自分の電話番号を入力し、SMS (Twilio など) で確認コードを受け取り、このコードを検証して自分の番号を確認することができます。

主な手順:

  1. 必要な依存関係をインストールします
  2. 電話番号を含めるようにユーザー テンプレートを編集します
  3. 確認コードを保存するテンプレートを作成する
  4. SMS 送信サービス (Twilio など) を設定します
  5. DRF シリアライザーの作成
  6. ビューと API ルートを作成する
  7. 検証ロジックとセキュリティを管理する

1. 必要な依存関係をインストールする

まず、必要なライブラリがインストールされていることを確認してください:

  • Django REST フレームワーク: まだお持ちでない場合。
  • Twilio: SMS の送信用。
  • django-phonenumber-field: 電話番号の検証とフォーマット用。

pip 経由でインストールします:

pip install djangorestframework twilio django-phonenumber-field
ログイン後にコピー
ログイン後にコピー

settings.pyのINSTALLED_APPSにphonenumber_fieldとrest_frameworkを追加します:

# settings.py

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'phonenumber_field',
    # ...
]
ログイン後にコピー
ログイン後にコピー

2. 電話番号を含めるようにユーザー テンプレートを変更します

カスタム ユーザー テンプレートを使用している場合は、電話番号と確認フラグのフィールドを追加します。

# 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 フィールドを適切に追加してください。

3. 検証コードを保存するためのテンプレートを作成する

このテンプレートには、ユーザーに送信される確認コードが保存されます。

# 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'}"
ログイン後にコピー
ログイン後にコピー

4. SMS 送信サービス (例: Twilio) を構成する

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
ログイン後にコピー
ログイン後にコピー

5. DRF シリアライザーの作成

検証リクエストとコード検証を処理するシリアライザーを作成します。

pip install djangorestframework twilio django-phonenumber-field
ログイン後にコピー
ログイン後にコピー

6. API ビューとルートの作成

検証リクエストとコード検証を管理するためのビューを作成します。

# settings.py

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'phonenumber_field',
    # ...
]
ログイン後にコピー
ログイン後にコピー

注: 検証中にユーザーを作成する場合や、既存のユーザーを別の方法で管理する場合など、必要に応じてこれらのビューを調整できます。

7. API ルートの構成

対応するルートを 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
ログイン後にコピー
ログイン後にコピー

8. ロジックを追加する (オプション)

もっている。ユーザーごとに固有のコードの生成

リクエスト ビューを編集して、コードを特定のユーザーに関連付けるか、新しいユーザーを作成します。

b.リクエストの数を制限する

悪用を避けるために、ユーザーまたは電話番号ごとの確認リクエストの数を制限します。

# 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'}"
ログイン後にコピー
ログイン後にコピー

c.検証中のユーザー管理

検証後にユーザーを作成するか、番号を既存のユーザーに関連付けるかを決定できます。

9. テストと検証

システムを運用環境にデプロイする前に、必ず開発環境でシステムをテストしてください。以下を確認してください:

  • SMS メッセージは正しく送信されます。
  • コードは生成され、安全に保存されます。
  • 小切手は設定された時間が経過すると期限切れになります。
  • エラーは正しく処理され、ユーザーに通知されます。

完全な実装例

概要を説明するために、影響を受けるファイルの完全な例を次に示します。

モデル.py

# settings.py

TWILIO_ACCOUNT_SID = 'votre_account_sid'
TWILIO_AUTH_TOKEN = 'votre_auth_token'
TWILIO_FROM_NUMBER = '+1234567890'  # Numéro Twilio
ログイン後にコピー
ログイン後にコピー

シリアライザー.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
ログイン後にコピー
ログイン後にコピー

views.py

# 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
ログイン後にコピー

URL.py

# 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)
ログイン後にコピー

utils.py

# 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'),
]
ログイン後にコピー

10. 安全性と最適化

  • 検証の試行を制限する: ブルート フォース攻撃を回避するために、検証の試行回数を制限するシステムを実装します。

  • コードの暗号化: セキュリティを強化するために、データベース内の確認コードを暗号化できます。

  • 非同期タスクを使用する: パフォーマンスを向上させるには、非同期タスク (Celery など) を使用して、API リクエストをブロックせずに SMS を送信します。

  • HTTPS の構成: 通信を保護するために、API が HTTPS 経由でアクセスできることを確認してください。

以上がDRF プロジェクトでの電話番号検証の実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート