DRFにてログイン認証。authenticateの正常系がうまく返らない
解決したいこと
新規登録画面、ログイン、ログアウトをDjango Rest Framework(DRF)で実装していますが、ログインの際に authenticate メソッドが正しく機能しない問題が発生しています。正しいユーザー名とパスワードを入力しても、None が返されてしまいます。
発生している問題・エラー
シリアライザの中で authenticate(username=username, password=password) に正しい値が渡されているにもかかわらず、None が返されてしまいます。
authenticate(username=username, password=password)には正しいパスワードとユーザーを入れているにもかかわらず、Noneが返されるのが今の問題です。
username = data.get('username')
password = data.get('password')は正しい値を取得できてます
def validate(self, data):
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
# 認証成功
print("認証成功:", user)
return data
else:
# 認証失敗
print("認証失敗")
raise serializers.ValidationError('some_field は無効な値です。')
API情報
<!-- 登録 -->
curl -X POST -H "Content-Type: application/json" -d "{\"register\": {\"username\": \"exam89\", \"email\": \"user@example.com\", \"password\": \"secret_password\"}, \"other_data\": {\"birth_day\": \"1990-01-01\", \"favorite\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}" http://localhost:8000/users/register/
<!-- ログイン -->
curl -X POST -H "Content-Type: application/json" -d "{\"username\": \"exam89\", \"email\": \"user@example.com\",\"password\": \"secret_password\"}" http://localhost:8000/users/login/
ソースコード
serializer.py
import logging
# ロガーの設定
logger = logging.getLogger(__name__)
from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
# 例: views.py などでのパスワードの更新
from .models import UserInfo
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = UserInfo
fields = ('user','birth_day','favorite','created_at')
class LoginSerializer(serializers.Serializer):
# 1.受け入れるシリアライザデータを作成する(fields)
username = serializers.CharField(max_length=255, write_only=True)
password = serializers.CharField(write_only=True, style={'input_type': 'password'})
def validate(self, data):
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
# 認証成功
print("認証成功:", user)
return data
else:
# 認証失敗
print("認証失敗")
raise serializers.ValidationError('some_field は無効な値です。')
view.py
from rest_framework import viewsets
from rest_framework.views import APIView
from .models import UserInfo,User
from .serializer import UserInfoSerializer, UserSerializer,LoginSerializer
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework_simplejwt.tokens import AccessToken
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import generics
from django.contrib.auth.hashers import make_password
from django.db import transaction
import logging
logger = logging.getLogger(__name__)
class UserInfoViewSet(viewsets.ModelViewSet):
queryset = UserInfo.objects.all()
serializer_class = UserInfoSerializer
@transaction.atomic
def create(self, request, *args, **kwargs):
user_data = request.data.get('register')
userinfo_data = request.data.get('other_data')
user_serializer = UserSerializer(data=user_data)
logging.debug("debug")
if user_serializer.is_valid():
# パスワードを変換
user_serializer.validated_data['password'] = make_password(user_serializer.validated_data['password'])
user = user_serializer.save()
userinfo_data['user'] = user.id
userinfo_serializer = UserInfoSerializer(data=userinfo_data)
if userinfo_serializer.is_valid():
# ここでデータを保存する
userinfo_serializer.save()
return Response(data=user_serializer.data, status=201)
else:
return Response(userinfo_serializer.data, userinfo_serializer.errors)
else:
return Response(user_serializer.data, user_serializer.errors)
# else:
# logger.error(f"UserInfoSerializer validation error: {userinfo_serializer.errors}")
class LoginAPIView(generics.GenericAPIView):
logging.debug("debug")
permission_classes = [AllowAny]
# ここにシリアライザを定義しておくと、以下のように、シリアライザを簡単に呼び出す
serializer_class = LoginSerializer
@transaction.atomic
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
print("ログイン:serializer")
if serializer.is_valid(raise_exception=True):
user = User.objects.get(username=serializer.validated_data["username"])
return Response({'detail': "ログインが成功しました。", 'error': 0})
# return Response({'detail': "ログインが成功しました。", 'error': 0, 'token': token.token, 'user_id': user_id})
return Response({'error': 1}, status=HTTP_400_BAD_REQUEST)
url.py
import logging
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from rest_framework.authtoken.views import obtain_auth_token
from sayhellotobikeAPI.views import UserInfoViewSet, LoginAPIView
# DefaultRouter クラスのインスタンスを代入
defaultRouter = routers.DefaultRouter()
# userInfo/ にUserInfoViewSetをルーティングする
defaultRouter.register('register', UserInfoViewSet, basename='register')
# UserLoginView
urlpatterns = [
path('admin/', admin.site.urls),
# ログイン用のエンドポイントを追加
path('users/login/', LoginAPIView.as_view(), name='login'),
# defaultRouter をinclude する
path('users/', include(defaultRouter.urls)),
]
model.py
from django.db import models
from django.contrib.auth.models import User
class UserInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_day = models.DateField(verbose_name='生年月日')
favorite = models.CharField(verbose_name='お気に入りの', max_length=100, default='特になし') # favorite フィールドを追加
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
def __str__(self):
return self.user.username
自分で試したこと
authincate関数はこちらで調べました。
https://docs.djangoproject.com/ja/2.2/_modules/django/contrib/auth/#authenticate
また、ヘイブンのままパスワードを保存していたので、ハッシュ化して保存しました
備考
私もまだまだなエンジニアですが、皆様公式ドキュメントを閲覧するとき、どのようなことを意識していますでしょうか?
ライブラリの場合は記述している内容だけではなく、ライブラリのソースコードを見るようにしてはいますが、、
ご回答いただけたら幸いです。よろしくお願いいたします。
0