首頁 >後端開發 >Python教學 >在drf專案中實現電話號碼驗證

在drf專案中實現電話號碼驗證

Mary-Kate Olsen
Mary-Kate Olsen原創
2024-12-28 10:17:10145瀏覽

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

要使用 Django REST Framework (DRF) 實作電話號碼驗證系統,您可以依照下列步驟操作。該系統將允許用戶提供他們的電話號碼,透過簡訊接收驗證碼(例如透過 Twilio),並驗證此代碼以驗證他們的號碼。

主要步驟:

  1. 安裝必要的依賴項
  2. 編輯使用者範本以包含電話號碼
  3. 建立範本來儲存驗證碼
  4. 設定簡訊發送服務(例如 Twilio)
  5. 建立 DRF 序列化器
  6. 建立視圖和 API 路由
  7. 管理驗證邏輯與安全性

1.安裝必要的依賴項

首先,請確保您已經安裝了必要的庫:

  • Django REST Framework:如果您還沒有。
  • Twilio:用於發送簡訊。
  • django-phonenumber-field:用於驗證和格式化電話號碼。

透過 pip 安裝它們:

pip install djangorestframework twilio django-phonenumber-field

將phonenumber_field和rest_framework加入settings.py中的INSTALLED_APPS中:

# 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. 設定簡訊發送服務(例如 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

建立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. 測試和驗證

在將系統部署到生產環境之前,請務必在開發環境中測試您的系統。檢查:

  • 簡訊已正確發送。
  • 程式碼是安全產生和儲存的。
  • 支票在設定時間後過期。
  • 錯誤被正確處理並傳達給使用者。

完整的實施範例

為了給您一個概述,這裡是受影響文件的完整範例。

模型.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

視圖.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

urls.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)

實用程式.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 請求。

  • 設定 HTTPS: 確保您的 API 可透過 HTTPS 存取以確保通訊安全。

以上是在drf專案中實現電話號碼驗證的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn