🍏

Django Rest FrameworkでJWTを使ってみる。

2023/09/05に公開

初めに

今回はDRFでJWT認証を使用してみようと思う。

環境

  • python version 3.9.17
  • Django version 4.2.3
  • Django Rest Framework version 3.14.0
  • Simple JWT ←JWT認証のパッケージ

ディレクトリ構造

ディレクト構造
drfTest/
├── accounts/
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   ├── views.py
├── drfTest/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
├── db.sqlite3
├── manage.py
accounts/models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email field must be set")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self.create_user(email, password, **extra_fields)

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(auto_now_add=True)
    password = models.CharField(max_length=128)

    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['password']

    def __str__(self):
        return self.email

SerializerとViewSetの設定

accounts/serializers.py
from rest_framework import serializers
from .models import CustomUser


class CustomUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = ['id', 'email', 'password']
        extra_kwargs = {'password': {'write_only': True}}
    
    #createメソッドをオーバーライドしている。
    def create(self, validated_data):
        user = CustomUser(
            email=validated_data['email'],
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

accounts/views.py
from rest_framework import viewsets, permissions, status
from rest_framework.response import Response
from .models import CustomUser
from .serializers import CustomUserSerializer

#permission_classesのオプションを新たに作成している。
#IsUserOrReadOnlyは、ユーザーが自分の情報を見ることはできるが、他のユーザーの情報を見ることはできないようにする。
class IsUserOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj == request.user

class CustomUserViewSet(viewsets.ModelViewSet):
    def get_queryset(self):
        return CustomUser.objects.filter(id=self.request.user.id)
    
    queryset = CustomUser.objects.all()
    serializer_class = CustomUserSerializer
    permission_classes = [permissions.IsAuthenticated, IsUserOrReadOnly]
    

urls.pyの設定

drfTest/urls.py
from django.urls import include, path
from rest_framework import routers
from django.conf import settings
from accounts import views as accounts_views

router = routers.DefaultRouter()
router.register(r'users', accounts_views.CustomUserViewSet)
urlpatterns = [
    path('', include(router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    #JWTのためのURL
    path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]

テストしてみる

ターミナルでrunserverする。

terminal
python manage.py runserver 8080

下記のコマンドを入力してトークンを取得してみる

terminal
 curl   -X POST   -H "Content-Type: application/json"   -d '{"email": "example@mmail.com", "password"
: "example"}'   http://localhost:8080/api/token/

example@mail.comとexampleは自身で作成したユーザのemailアドレスとパスワードにする。

結果

terminal
{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY5NjQzMzk5MiwiaWF0IjoxNjkzODQxOTkyLCJqdGkiOiJkMTlmMDI2NDliMWQ0NTY2OWY3NDQ0MGQxNzBkODkyYyIsInVzZXJfaWQiOjF9.aXeTuu5E7mhOAXxBHL7nEp2_Tefz4YKej2Bf8l7rrrU","access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjkzODQyODkyLCJpYXQiOjE2OTM4NDE5OTIsImp0aSI6IjY3NWNlYjg2YzNiMjQ3NDdiYWRkYzk1YzAxMTcyMzRmIiwidXNlcl9pZCI6MX0.s8IT39j8L0Ja7XCJm3HVe_re8FYkZixXY7PPUeE2lHA"}

アクセストークンとリフレッシュトークンが返ってきた。

Discussion