[DRF] DRF Django로 아이디/비밀번호 찾기 구현하기 - 5편

2022. 6. 4. 18:46

프로젝트를 진행하면서, drf으로 아이디와 비밀번호 찾기를 구현해보았다.

우리 프로젝트에서는 아이디를 이메일로 설정했기 때문에 아이디 찾기를 구현하기 어렵다는 문제가 있었다.

그래서 이메일 찾기는 이메일이 존재하는지 아닌지 만을 확인하여 띄워주고,

비밀번호는 이메일에 메일을 보내서 url로 연결한 다음 비밀번호 찾기를 하는 방식으로 구현하였다.

 

먼저 이메일 찾기 먼저 살펴보자.

이메일 찾기를 위한 serializer를 먼저 작성해보자.

 

class EmailFindSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=64,required=True)

 

간단하게 이메일만을 받는 serializer를 작성하였다.

그 다음 views.py에서 이 이메일이 존재하는 이메일인지, 아닌지를 내보내주는 함수를 작성하였다.

 

#이메일 찾기
@api_view(["PUT"])
@permission_classes([AllowAny])
def findemail(request):
    serializer = EmailFindSerializer(data=request.data)
    if serializer.is_valid():
        if User.objects.filter(email=serializer.data['email']).exists():
            return Response('존재하는 이메일입니다')
        else:
            return Response('존재하지 않는 이메일입니다')
    return Response('이메일을 다시 입력하세요')

 

filter를 사용하여 email이 존재하는 이메일인지, 아닌지를 확인하였다.

 

이 함수를 url에 등록해주자.

 

path('findid/',views.findemail),

 

그러면 완료!

 

다음 비밀번호 찾기이다. 

이메일 찾기와 마찬가지로 비밀번호 찾기를 위한 serializer를 작성해보자.

 

#비밀번호 이메일 보낼 때 쓰는 거
class PwEmailSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=64)

    def validate_email(self, value):
        '''데이터베이스에 존재하는지 확인'''
        if not User.objects.filter(email=value).exists():
            raise serializers.ValidationError("존재하지 않는 이메일입니다.")
        else:
            return value

#비밀번호 변경할 때 쓰는거
class PwChangeSerializer(serializers.Serializer):
    password = serializers.CharField(required=True)

 

위의 PwEmailSerializer는 이메일을 보낼 때 쓰는 serializer이고, 

아래의 PwChangeSerializer는 비밀번호를 변경할 때 쓰는 serializer이다.

 

#비밀번호 재설정 이메일 보내기
class PwResetEmailSendView(APIView):
    permission_classes = [AllowAny]
    
    def put(self,request):
        serializer = PwEmailSerializer(data=request.data)
        try:
            if serializer.is_valid():
                user_email = serializer.data['email']
                print(user_email)
                user = User.objects.get(email = user_email)
                print(user)
                payload = JWT_PAYLOAD_HANDLER(user)
                jwt_token = JWT_ENCODE_HANDLER(payload)
                message = render_to_string('users/password_reset.html', {
                    'user': user,
                    'domain': 'localhost:8000',
                    'uid': force_str(urlsafe_base64_encode(force_bytes(user.pk))),
                    'token': jwt_token,
                })
                print(message)
                mail_subject = '[SDP] 비밀번호 변경 메일입니다'
                to_email = user.email
                email = EmailMessage(mail_subject, message, to = [to_email])
                email.send()    
                return Response( user.email+ '이메일 전송이 완료되었습니다',status=status.HTTP_200_OK)
            print(serializer.errors)
            return Response('일치하는 유저가 없습니다',status=status.HTTP_400_BAD_REQUEST)
        except( ValueError, OverflowError, User.DoesNotExist):
            user = None
            print(traceback.format_exc())
            return Response('일치하는 유저가 없습니다',status=status.HTTP_400_BAD_REQUEST)

#비밀번호 재설정

class PasswordChangeView(APIView):
    
    model = User
    permission_classes = [AllowAny]

    def put(self, request, uid, token):
        serializer = PwChangeSerializer(data=request.data)
        if serializer.is_valid():
            real_uid = force_str(urlsafe_base64_decode(uid))
            user = User.objects.get(pk=real_uid)
            if user is not None:
                payload = jwt_decode_handler(token)
                user_id =jwt_payload_get_user_id_handler(payload)
                if int(real_uid) == int(user_id):
                    print("비밀번호")
                    print(user.password)
                    print(serializer.data['password'])
                    if serializer.data['password']:
                        user2 = authenticate(email=user.email, password=serializer.data['password'])
                        if user2 != None :
                            return Response('기존 비밀번호와 일치합니다',status=status.HTTP_400_BAD_REQUEST)
                        user.set_password(serializer.data.get("password"))
                        print(user.password)
                        user.save()
                        response = {
                            'status': 'success',
                            'code': status.HTTP_200_OK,
                            'message': 'Password updated successfully',
                            'data': []
                        }
                        return Response(response)
                    return Response('비밀번호를 다시 입력해주세요',status=status.HTTP_400_BAD_REQUEST)
                return Response('인증에 실패하였습니다',status=status.HTTP_400_BAD_REQUEST)
            return Response('일치하는 유저가 없습니다',status=status.HTTP_400_BAD_REQUEST)           
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

 

위의 PwResetEmailSendView는 4편에서의 이메일인증을 보낼 때와 마찬가지로 

JWT를 생성하여 email에 넣어서 보내주었다.

password_reset.html을 작성해주어서 email에 넣을 내용을 작성해주어야 한다.

 

{% autoescape off %}

사이트로 돌아가 비밀번호를 재설정해주시기 바랍니다.

http://{{ domain }}{% url 'users:pwchange' uid=uid token=token %}

{% endautoescape %}

 

그 다음 url을 클릭하면 password를 재설정할 수 있는 PasswordChangeView가 작동한다. 

이 함수에서는 user.set_password()를 이용하여 password를 재설정해준다.

여기서 한가지 주의할 점은, 바꾸는 비밀번호가 원래의 비밀번호와 같을 때 재설정을 막아줘야 한다는 것이다.

우리는 Django의 암호화 방법을 사용하여 비밀번호를 암호화하여 저장했고 Django에서는 양방향 암호화, 복호화를 지원하지 않기 때문에 어떻게 이를 구현해야 하는지 헷갈렸다.

하지만 그냥 간단하게 장고의 authenticate를 사용하여 새로 받은 비밀번호로 authenticate가 되면 

기존의 비밀번호와 일치한다는 message를 return 하였다.

 

최종적으로 작성한 함수를 url에 넣어주었다.

path('find_pw/',views.PwResetEmailSendView.as_view()),
path('pwchange/<str:uid>/<str:token>',views.PasswordChangeView.as_view(), name ='pwchange'),

 

비밀번호 찾기도 완료되었다!

 

BELATED ARTICLES

more