5
votes

Comment puis-je utiliser `email` dans" django-rest-framework-simplejwt "au lieu de` username` pour générer un jeton?

Dans le plugin django-rest-framework-simplejwt, username et password sont utilisés par défaut. Mais je voulais utiliser email au lieu de username . Donc, j'ai fait comme ci-dessous:

Dans le sérialiseur:

class MyTokenObtainPairView(TokenObtainPairView):
   """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
   """
    serializer_class = MyTokenObtainPairSerializer

En vue:

XXX

Et ça marche !!

Maintenant ma question est, comment puis-je le faire plus efficacement? Quelqu'un peut-il donner une suggestion à ce sujet? Merci d'avance.


0 commentaires

4 Réponses :


4
votes

Cette réponse est destinée aux futurs lecteurs et contient donc des informations supplémentaires.

Afin de simplifier le backend d'authentification, vous devez vous connecter à plusieurs classes. Je suggère de faire l ' option 1 (et éventuellement l' option 3 , une version simplifiée de la vôtre) ci-dessous. Quelques notes avant de lire:

  • Remarque 1: django n'applique pas les e-mails comme requis ou n'est pas unique lors de la création de l'utilisateur (vous pouvez remplacer cela, mais c'est hors sujet)! L'option 3 (votre implémentation) peut donc vous poser des problèmes avec les e-mails en double.
  • Remarque 1b: utilisez User.objects.filter (email__iexact = ...) pour faire correspondre les e-mails sans respecter la casse.
  • Remarque 1c: utilisez get_user_model () au cas où vous remplaceriez le modèle utilisateur par défaut à l'avenir, c'est vraiment une bouée de sauvetage pour les débutants!
  • Remarque 2: évitez d'imprimer l'utilisateur sur la console. Vous imprimez peut-être des données sensibles.

Quant aux 3 options:

  1. Ajustez le backend d'authentification django avec f.e. class EmailModelBackend (ModelBackend) et remplacez la fonction d'authentification.
    • N'ajuste pas les revendications de jetons
    • Ne dépend pas de la classe / middleware JWT (SimpleJWT, JWT ou autres)
    • Ajuste également d'autres types d'authentification (Session / Cookie / Auth non-API, etc.)
    • Le paramètre d'entrée requis est toujours nom d'utilisateur , exemple ci-dessous. Ajustez-le si vous ne l'aimez pas, mais faites-le avec précaution. (Peut casser vos importations / plugins et n'est pas nécessaire!)
  2. Remplacez django authenticate (username =, password =, ** kwarg) de django.contrib.auth
    • N'ajuste pas les revendications de jetons
    • Vous devez également remplacer le backend du jeton, car il doit utiliser une authentification différente, comme vous l'avez fait ci-dessus.
    • N'ajuste pas les autres applications en utilisant authenticate (...) , remplace uniquement l'authentification JWT (si vous la configurez comme telle) n'est pas obligatoire et cette option est donc moins conseillée).
  3. Implémentez MyTokenObtainPairSerializer avec l'e-mail comme revendication.
    • Les e-mails sont désormais renvoyés sous forme de données JWT (et non d'identifiant).
    • Avec l'option 1, l'authentification de votre application est devenue indépendante du nom d'utilisateur.

Option 1 (notez que cela autorise également le nom d'utilisateur !!):

SIMPLE_JWT = {
    'USER_ID_FIELD': 'id', # model property to attempt claims for
    'USER_ID_CLAIM': 'user_id', # actual keyword in token data
}

Option 2: fort> Ignoré, laissé au lecteur et non conseillé.

Option 3: Vous semblez avoir couvert cela ci-dessus.

Remarque: vous n'avez pas à définir MyTokenObtainPairView , vous pouvez utiliser TokenObtainPairView (serializer_class = MyTokenObtainPairSerializer) .as_view () dans votre urls.py. Petite simplification qui remplace le sérialiseur de jetons utilisé.

Remarque 2: Vous pouvez spécifier la revendication d'identification et les données ajoutées dans vos paramètres.py (ou fichier de paramètres) pour utiliser également le courrier électronique. Cela obligera votre application frontend à utiliser également l'e-mail pour la réclamation (au lieu de l'adresse par défaut user)

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class EmailorUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

Cependant, tenez compte des avertissements d'unicité donné par les créateurs:

Par exemple, spécifier un champ "nom d'utilisateur" ou "e-mail" serait un mauvais choix car le nom d'utilisateur ou l'adresse e-mail d'un compte peut changer en fonction de la façon dont la gestion du compte dans un service donné est conçue.

Si vous pouvez garantir l'unicité, vous êtes prêt.


0 commentaires

4
votes

Pourquoi avez-vous copié et collé autant au lieu de sous-classer? Je l'ai fait fonctionner avec:

#urls.py
from rest_framework_simplejwt.views import TokenRefreshView
from .views import EmailTokenObtainPairView

url("token/", EmailTokenObtainPairView.as_view(), name="token_obtain_pair"),
url("refresh/", TokenRefreshView.as_view(), name="token_refresh"),

Et

# views.py
from rest_framework_simplejwt.views import TokenObtainPairView

class EmailTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

Et bien sûr

# serializers.py 
from rest_framework_simplejwt.serializers import TokenObtainSerializer

class EmailTokenObtainSerializer(TokenObtainSerializer):
    username_field = User.EMAIL_FIELD


class CustomTokenObtainPairSerializer(EmailTokenObtainSerializer):
    @classmethod
    def get_token(cls, user):
        return RefreshToken.for_user(user)

    def validate(self, attrs):
        data = super().validate(attrs)

        refresh = self.get_token(self.user)

        data["refresh"] = str(refresh)
        data["access"] = str(refresh.access_token)

        return data


0 commentaires

0
votes

Et en plus de la réponse de @ Mic, n'oubliez pas de définir USERNAME_FIELD = 'email' et peut être REQUIRED_FIELDS = ['username'] dans le modèle utilisateur.


0 commentaires

0
votes

La question fait longtemps mais j'ajoute +1 pour la réponse de @ Mic. Au fait, n'était-il pas suffisant de mettre à jour vers TokenObtainPairSerializer uniquement comme suit?:

from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import (
    TokenObtainPairSerializer, User
)

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    username_field = User.EMAIL_FIELD


class EmailTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer


0 commentaires