J'ai créé l'API suivante en utilisant Django REST Framework, alors prenez note.
--S'identifier --Utilisateur créé
Même si je l'ai fait, la partie que j'ai faite avec une atmosphère relativement grande est grande, et il y a aussi le problème de savoir si c'est optimal, donc c'est pour référence seulement.
Vous pouvez utiliser le modèle utilisateur par défaut pour le modèle, mais si vous souhaitez créer une application réelle, vous devrez la personnaliser, j'ai donc créé le modèle suivant.
user/models.py
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser, _user_has_perm
)
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
class AccountManager(BaseUserManager):
def create_user(self, request_data, **kwargs):
now = timezone.now()
if not request_data['email']:
raise ValueError('Users must have an email address.')
profile = ""
if request_data.get('profile'):
profile = request_data['profile']
user = self.model(
username=request_data['username'],
email=self.normalize_email(request_data['email']),
is_active=True,
last_login=now,
date_joined=now,
profile=profile
)
user.set_password(request_data['password'])
user.save(using=self._db)
return user
def create_superuser(self, username, email, password, **extra_fields):
request_data = {
'username': username,
'email': email,
'password': password
}
user = self.create_user(request_data)
user.is_active = True
user.is_staff = True
user.is_admin = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
username = models.CharField(_('username'), max_length=30, unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
profile = models.CharField(_('profile'), max_length=255, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def user_has_perm(user, perm, obj):
return _user_has_perm(user, perm, obj)
def has_perm(self, perm, obj=None):
return _user_has_perm(self, perm, obj=obj)
def has_module_perms(self, app_label):
return self.is_admin
def get_short_name(self):
return self.first_name
@property
def is_superuser(self):
return self.is_admin
class Meta:
db_table = 'api_user'
swappable = 'AUTH_USER_MODEL'
L'utilisateur admin peut être créé comme d'habitude avec $ python manage.py createuperuser
.
Les deux points suivants sont différents de la valeur par défaut.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
profile = models.CharField(_('profile'), max_length=255, blank=True)
c'est tout.
Cette fois, nous avons implémenté l'authentification par jeton par JWT. Installez django-rest-framework-jwt avec pip.
$ pip install djangorestframework-jwt
Après l'installation, ajoutez ce qui suit à settings.py.
djangoproject/settings.py
JWT_AUTH = {
'JWT_VERIFY_EXPIRATION': False,
'JWT_AUTH_HEADER_PREFIX': 'JWT',
}
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'NON_FIELD_ERRORS_KEY': 'detail',
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}
Si vous définissez `` JWT_VERIFY_EXPIRATION ': False, `, la date d'expiration du jeton sera permanente.
Ensuite, accédez à url.py comme suit
djangoproject/urls.py
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^login/', obtain_jwt_token),
]
Avec cela seul, vous pouvez obtenir un jeton en POSTANT sur / en vous connectant à l'aide d'un e-mail et d'un mot de passe.
Tout d'abord, nous avons besoin d'un point de terminaison d'API, alors créez urls.py dans l'application et
user/urls.py
from django.conf.urls import include, url
from rest_framework import routers
from .views import AuthRegister
urlpatterns = [
url(r'^register/$', AuthRegister.as_view()),
]
Chargez-le dans urls.py côté projet.
djangoproject/urls.py
from django.conf.urls import include, url
from django.contrib import admin
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^login/', obtain_jwt_token),
url(r'^api/', include('authentication.urls')),
]
POST dans / api / register / pour permettre la création d'utilisateurs
user/views.py
from django.contrib.auth import authenticate
from django.db import transaction
from django.http import HttpResponse, Http404
from rest_framework import authentication, permissions, generics
from rest_framework_jwt.settings import api_settings
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
from rest_framework import status, viewsets, filters
from rest_framework.views import APIView
from .serializer import AccountSerializer
from .models import Account, AccountManager
#Vue créée par l'utilisateur(POST)
class AuthRegister(generics.CreateAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Account.objects.all()
serializer_class = AccountSerializer
@transaction.atomic
def post(self, request, format=None):
serializer = AccountSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
En utilisant des génériques, vous pouvez limiter les méthodes HTTP qu'une classe peut accepter. Pour plus d'informations sur les génériques, voir ici. CreateAPIView est utilisé pour les points de terminaison de création uniquement.
De plus, bien sûr, pour la création d'un utilisateur, il est nécessaire de POST sur le point final même s'il n'est pas authentifié, donc permission_classes = (permissions.AllowAny,)
est défini.
from django.contrib.auth import update_session_auth_hash
from rest_framework import serializers
from .models import Account, AccountManager
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
class Meta:
model = Account
fields = ('id', 'username', 'email', 'profile', 'password')
def create(self, validated_data):
return Account.objects.create_user(request_data=validated_data)
En gros, cela ressemble à appeler une méthode créée par l'utilisateur définie dans Model.
Pour le moment, si vous POST email, mot de passe, profil (optionnel) json vers / api / register / dans l'état ci-dessus, un utilisateur sera créé.
Concernant l'acquisition des informations utilisateur, cela dépend de l'application, mais pour le moment, l'utilisateur connecté ne peut acquérir que ses propres informations. Si vous souhaitez obtenir des informations sur d'autres utilisateurs, vous devez créer une autre vue et un autre point de terminaison, je suis donc désolé.
Modifiez urls.py côté application.
user/urls.py
from django.conf.urls import include, url
from rest_framework import routers
from .views import AuthRegister, AuthInfoGetView
urlpatterns = [
url(r'^register/$', AuthRegister.as_view()),
url(r'^mypage/$', AuthInfoGetView.as_view()),
]
Obtenez des informations utilisateur lorsque vous lancez un GET vers / api / mypage.
Ajoutez ce qui suit à views.py plus tôt
user/views.py
#Vue de l'acquisition d'informations utilisateur(GET)
class AuthInfoGetView(generics.RetrieveAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get(self, request, format=None):
return Response(data={
'username': request.user.username,
'email': request.user.email,
'profile': request.user.profile,
},
status=status.HTTP_200_OK)
RetrieveAPIView est un point de terminaison dédié à la méthode GET.
De plus, comme j'essaie d'obtenir mes propres informations, j'essaie de les obtenir uniquement lorsque je suis connecté avec permission_classes = (permissions.IsAuthenticated,)
.
Dans cet état, ajoutez {'Content-Type': 'application / json', 'Authorization': 'JWT [token obtenu à la connexion]'}
à l'en-tête et lancez la méthode GET pour vous connecter. Vous pouvez obtenir le nom d'utilisateur / email / profil de l'utilisateur actuel.
On parle d'utiliser PUT ou PATCH lors de la mise à jour des informations, mais pour le moment j'ai utilisé PUT cette fois, mais maintenant que j'y pense, je pense que PATCH est bon. Cependant, depuis que je l'ai fait, j'en parlerai dans PUT.
Créez un point de terminaison appelé / auth_update et appelez la vue AuthInfoUpdateView.
user/urls.py
from django.conf.urls import include, url
from rest_framework import routers
from .views import AuthRegister, AuthInfoGetView, AuthInfoUpdateView
urlpatterns = [
url(r'^register/$', AuthRegister.as_view()),
url(r'^mypage/$', AuthInfoGetView.as_view()),
url(r'^auth_update/$', AuthInfoUpdateView.as_view()),
]
Ajoutez ce qui suit à views.py. Il s'agit d'une implémentation d'AuthInfoUpdateView, une vue qui est appelée lorsqu'il y a un PUT dans / auth_update. Étant donné que l'e-mail est utilisé comme clé, l'e-mail de l'utilisateur connecté doit également être inclus dans le JSON lors de l'envoi d'un PUT.
user/views.py
#Vue de la mise à jour des informations utilisateur(PUT)
class AuthInfoUpdateView(generics.UpdateAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = AccountSerializer
lookup_field = 'email'
queryset = Account.objects.all()
def get_object(self):
try:
instance = self.queryset.get(email=self.request.user)
return instance
except Account.DoesNotExist:
raise Http404
Comme son nom l'indique, UpdateAPIView accepte PUT / PATCH comme une vue de mise à jour uniquement.
Modifiez AccountSerializer dans serializers.py comme suit.
user/serializers.py
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
class Meta:
model = Account
fields = ('id', 'username', 'email', 'profile', 'password')
def create(self, validated_data):
return Account.objects.create_user(request_data=validated_data)
def update(self, instance, validated_data):
if 'password' in validated_data:
instance.set_password(validated_data['password'])
else:
instance = super().update(instance, validated_data)
instance.save()
return instance
Si le mot de passe est mis à jour, il est nécessaire de hacher le mot de passe reçu, utilisez donc la méthode set_password.
Comme pour obtenir des informations utilisateur, vous devez METTRE avec {'Content-Type': 'application / json', 'Authorization': 'JWT [token obtenu à la connexion]'}
dans l'en-tête.
Enfin, nous mettrons en œuvre la suppression de l'utilisateur connecté.
Je crée un point de terminaison appelé / delete et j'appelle AuthInfoDeleteView.
user/urls.py
from django.conf.urls import include, url
from rest_framework import routers
from .views import AuthRegister, AuthInfoGetView, AuthInfoUpdateView, AuthInfoDeleteView
urlpatterns = [
url(r'^register/$', AuthRegister.as_view()),
url(r'^mypage/$', AuthInfoGetView.as_view()),
url(r'^auth_update/$', AuthInfoUpdateView.as_view()),
url(r'^delete/$', AuthInfoDeleteView.as_view()),
]
Une implémentation d'AuthInfoDeleteView.
user/views.py
#Vue de suppression de l'utilisateur(DELETE)
class AuthInfoDeleteView(generics.DestroyAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = AccountSerializer
lookup_field = 'email'
queryset = Account.objects.all()
def get_object(self):
try:
instance = self.queryset.get(email=self.request.user)
return instance
except Account.DoesNotExist:
raise Http404
En définissant generics.DestroyAPIView
, la vue accepte uniquement la méthode DELETE.
En envoyant DELETE avec {'Content-Type': 'application / json', 'Authorization': 'JWT [token obtenu à la connexion]'}
dans l'en-tête ainsi que l'acquisition et la mise à jour des informations utilisateur Vous pouvez supprimer l'utilisateur connecté.
c'est tout.
En fait, le courrier électronique ne peut pas être mis à jour dans l'état actuel. Non, je peux le faire, mais comme le jeton que j'ai côté client est celui connecté avec l'ancien e-mail, le comportement sera étrange si le côté client ne dispose pas du jeton obtenu avec le nouvel e-mail. Vous pouvez émettre un nouveau jeton avec un nouvel e-mail en POSTANT à / login avec un mot de passe et un nouvel e-mail, mais c'est absurde. Vous devez avoir le mot de passe en texte brut côté client. Pour le moment, je voudrais étudier cela plus en détail.
Aussi, je pense qu'il est nécessaire de préparer une API pour réémettre le mot de passe afin d'oublier le mot de passe, mais c'était ennuyeux </ s>, j'ai donc décidé de ne pas le faire cette fois.
En outre, le problème évident est que si le jeton de connexion est obtenu d'une manière ou d'une autre, les informations utilisateur peuvent être facilement altérées ou supprimées. Cependant, je pense que le problème du vol malveillant de jetons est une question de JWT. Cette fois, la date d'expiration du jeton est infinie, mais je me demande s'il est nécessaire de définir une date d'expiration ou d'émettre un jeton d'actualisation lors de l'envoi d'une requête à l'API lors de la publication effective de l'application. Je pense.
De plus, l'API ci-dessus combinée à Angular à l'avant est disponible dans le référentiel suivant. https://github.com/xKxAxKx/auth_django_and_angular2
Recommended Posts