Ich habe die folgende API mit Django REST Framework erstellt. Machen Sie sich also eine Notiz.
--Einloggen --Benutzer erstellt
Obwohl ich es gemacht habe, ist der Teil, den ich mit einer relativ großen Atmosphäre gemacht habe, groß, und es gibt auch das Problem, ob dies optimal ist, so dass es nur als Referenz dient.
Sie können das Standardbenutzermodell für das Modell verwenden. Wenn Sie jedoch eine tatsächliche Anwendung erstellen möchten, müssen Sie diese anpassen. Daher habe ich das folgende Modell erstellt.
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'
Der Administrator kann wie gewohnt mit "$ python manage.py createduperuser" erstellt werden.
Die folgenden zwei Punkte unterscheiden sich vom Standard.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
profile = models.CharField(_('profile'), max_length=255, blank=True)
das ist alles.
Dieses Mal haben wir die Token-Authentifizierung durch JWT implementiert. Installieren Sie django-rest-framework-jwt mit pip.
$ pip install djangorestframework-jwt
Fügen Sie nach der Installation Folgendes zu settings.py hinzu.
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'
}
Wenn Sie "JWT_VERIFY_EXPIRATION" auf "False" setzen, ist das Ablaufdatum des Tokens dauerhaft.
Gehen Sie als Nächstes wie folgt zu url.py.
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),
]
Allein damit können Sie ein Token erhalten, indem Sie per E-Mail und Passwort an / login senden.
Zunächst benötigen wir einen API-Endpunkt, also erstellen Sie urls.py in der Anwendung und
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()),
]
Laden Sie es auf der Projektseite in urls.py.
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 an / api / register /, um die Benutzererstellung zu ermöglichen
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
#Vom Benutzer erstellte Ansicht(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)
Mithilfe von Generika können Sie die HTTP-Methoden einschränken, die eine Klasse akzeptieren kann. Weitere Informationen zu Generika finden Sie hier (http://www.django-rest-framework.org/api-guide/generic-views/). CreateAPIView wird nur für Endpunkte zum Erstellen verwendet.
Natürlich ist es für die Benutzererstellung auch erforderlich, auf den Endpunkt zu POSTEN, auch wenn dieser nicht authentifiziert ist. Daher wird allow_classes = (permissions.AllowAny,)
festgelegt.
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)
Grob gesagt fühlt es sich an, als würde man eine vom Benutzer erstellte Methode aufrufen, die in Model definiert ist.
Wenn Sie im obigen Zustand E-Mail, Passwort, Profil (optional) an / api / register / senden, wird vorerst ein Benutzer erstellt.
Die Erfassung von Benutzerinformationen hängt von der Anwendung ab. Derzeit kann der angemeldete Benutzer jedoch nur seine eigenen Informationen erfassen. Wenn Sie Informationen zu anderen Benutzern erhalten möchten, müssen Sie eine andere Ansicht und einen anderen Endpunkt erstellen. Es tut mir leid.
Bearbeiten Sie urls.py auf der Anwendungsseite.
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()),
]
Erhalten Sie Benutzerinformationen, wenn Sie ein GET nach / api / mypage werfen.
Fügen Sie views.py zuvor Folgendes hinzu
user/views.py
#Ansicht der Benutzerinformationserfassung(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 ist ein Endpunkt für die GET-Methode.
Da ich versuche, meine eigenen Informationen abzurufen, versuche ich auch, sie nur abzurufen, wenn ich mit allow_classes = (permissions.IsAuthenticated,)
angemeldet bin.
Fügen Sie in diesem Status "{'Content-Type': 'application / json', 'Authorization': 'JWT [beim Anmelden erhaltenes Token' '}" zum Header hinzu und werfen Sie die GET-Methode, um sich anzumelden. Sie können den Benutzernamen / die E-Mail-Adresse / das Profil des aktuellen Benutzers abrufen.
Es ist die Rede davon, ob beim Aktualisieren von Informationen PUT oder PATCH verwendet werden soll, aber vorerst habe ich diesmal PUT verwendet, aber jetzt, wo ich darüber nachdenke, denke ich, dass PATCH gut ist. Da ich es jedoch geschafft habe, werde ich in PUT darüber sprechen.
Erstellen Sie einen Endpunkt mit dem Namen / auth_update und rufen Sie die Ansicht AuthInfoUpdateView auf.
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()),
]
Fügen Sie views.py Folgendes hinzu. Dies ist eine Implementierung von AuthInfoUpdateView, eine Ansicht, die aufgerufen wird, wenn sich in / auth_update ein PUT befindet. Da E-Mail als Schlüssel verwendet wird, muss die E-Mail des angemeldeten Benutzers beim Senden eines PUT auch im JSON enthalten sein.
user/views.py
#Ansicht der Aktualisierung der Benutzerinformationen(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
Wie der Name schon sagt, akzeptiert UpdateAPIView PUT / PATCH als reine Update-Ansicht.
Bearbeiten Sie AccountSerializer in serializers.py wie folgt.
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
Wenn das Kennwort aktualisiert wird, muss das empfangene Kennwort gehasht werden. Verwenden Sie daher die Methode set_password. Wie beim Abrufen von Benutzerinformationen müssen Sie im Header "{'Content-Type': 'application / json', 'Authorization': 'JWT [Token beim Anmelden erhalten]'}" eingeben.
Schließlich werden wir das Löschen des angemeldeten Benutzers implementieren.
Ich erstelle einen Endpunkt namens / delete und rufe AuthInfoDeleteView auf.
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()),
]
Eine Implementierung von AuthInfoDeleteView.
user/views.py
#Benutzer löschen Ansicht(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
Durch Festlegen von "generics.DestroyAPIView" akzeptiert die Ansicht nur die DELETE-Methode. Durch Senden von DELETE mit "{'Content-Type": "application / json", "Authorization": "JWT [Token beim Anmelden erhalten]"} "im Header sowie durch Erfassen und Aktualisieren von Benutzerinformationen Sie können den angemeldeten Benutzer löschen.
das ist alles.
Tatsächlich kann die E-Mail im aktuellen Status nicht aktualisiert werden. Nein, ich kann es tun, aber da das Token, das ich auf der Clientseite habe, mit der alten E-Mail angemeldet ist, ist das Verhalten seltsam, wenn auf der Clientseite das mit der neuen E-Mail erhaltene Token nicht vorhanden ist. Sie können ein neues Token mit einer neuen E-Mail ausstellen, indem Sie mit einem Kennwort und einer neuen E-Mail an / login senden, aber das ist Unsinn. Sie müssen das Passwort auf der Clientseite im Klartext haben. Ich möchte dies vorerst etwas genauer untersuchen.
Ich denke auch, dass es notwendig ist, eine API für die Neuausgabe des Passworts vorzubereiten, um das Passwort zu vergessen, aber das war ärgerlich </ s>, also habe ich beschlossen, es diesmal nicht zu tun.
Das offensichtliche Problem besteht auch darin, dass die Benutzerinformationen leicht manipuliert oder gelöscht werden können, wenn das Anmeldetoken auf irgendeine Weise erhalten wird. Ich denke jedoch, dass das Problem des böswilligen Raubes von Token eine Angelegenheit von JWT ist. Dieses Mal ist das Ablaufdatum des Tokens unendlich, aber ich frage mich, ob es notwendig ist, ein Ablaufdatum festzulegen oder ein Aktualisierungstoken auszugeben, wenn eine Anforderung an die API gesendet wird, wenn die Anwendung tatsächlich veröffentlicht wird. Ich denke.
Darüber hinaus ist die oben genannte API in Kombination mit Angular auf der Vorderseite im folgenden Repository verfügbar. https://github.com/xKxAxKx/auth_django_and_angular2
Recommended Posts