훈훈훈

파이썬(Django) :: 회원 가입 및 로그인 API 구현 본문

파이썬/Django

파이썬(Django) :: 회원 가입 및 로그인 API 구현

훈훈훈 2020. 2. 15. 20:55

장고(Django)를 이용하여 간단하게 회원가입과 로그인 기능을 구현한 API에 패스워드 암호화 및 토큰 기능을 추가해보았다.

 

 * 이전에 구현한 코드 

https://wave1994.tistory.com/57

 

# models.py

models.py 파일은 데이터베이스 테이블을 만들고 그 안의 필드들을 생성 및 수정할 수 있는 역할을 한다.

from django.db import models

class Account(models.Model):
    email      = models.EmailField(max_length=100, unique=True)
    password   = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'accounts'

models.py 은 데이터베이스 테이블 생성과 관련 파일이기 떄문에 이전 코드에서 수정한 내역은 없다.

 

# view.py

view.py은 models.py에서 만든 DB 테이블의 데이터를 처리하는 로직을 만들 수 있다.

이번에 수정한 파일은 view.py이며 해당 파일에 패스워드 암호화 및 토큰 발급 기능을 추가하였다.

 

- 모듈

import json
import bcrypt
import jwt

from .models         import Account
from mysite.settings import SECRET_KEY

from django.views    import View
from django.http     import HttpResponse, JsonResponse

이전에 작성한 코드에서 추가적으로 bcrypt, jwt 모듈과 외부에 저장해놓은 SECRET_KEY 파일을 임포트하였다.

패스워드를 단방향으로 암호화 하기 위해 bcrypt라는 해시 함수, 그리고 인증(인가)을 위해 JWT(Json Web Token)을 사용하려고 한다.

 

 * SECRET_KEY에 대한 이분은 이전 포스팅한 내용 참고바란다.

   - https://wave1994.tistory.com/64

 

- 클래스 

class SignUp(View):
    def post(self, request):
        data = json.loads(request.body)

        try:
            if Account.objects.filter(email = data['email']).exists():
                return JsonResponse({"message" : "EXISTS_EMAIL"}, status=400)
            
            Account.objects.create(
                email 	 = data['email'], 
                password = bcrypt.hashpw(data["password"].encode("UTF-8"), bcrypt.gensalt()).decode("UTF-8")
            ).save()

            return HttpResponse(status=200)
            
        except KeyError:
            return JsonResponse({"message" : "INVALID_KEYS"}, status=400)

위 코드는 회원가입 기능을 하는 클래스이며, 이전 코드에 패스워드 암호화 후 DB에 저장하는 기능을 추가하였다.

패스워드는 UTF-8로 인코딩 후 bcrypt 해시 함수를 이용하여 암호화 후 디코딩 후 DB에 저장하였다. (DB에는 string 값만 저장 가능)

 

class SignIn(View):
    def post(self, request):
        data = json.loads(request.body)

        try:
            if Account.objects.filter(email=data["email"]).exists():
                user = Account.objects.get(email=data["email"])

                if bcrypt.checkpw(data['password'].encode('UTF-8'), user.password.encode('UTF-8')):

                    token = jwt.encode({'user' : user.id}, SECRET_KEY, algorithm='HS256').decode('UTF-8')
                    
                    return JsonResponse({"token" : token}, status=200)

                return HttpResponse(status=401)

            return HttpResponse(status=400)
        
        except KeyError:
            return JsonResponse({'message' : "INVALID_KEYS"}, status=400)

위 코드는 로그인 기능을 하는 클래스이며, 이전 코드에 로그인 성공 시 토큰(JWT)을 발급하는 기능을 추가하였다.

토큰의 페이로드 부분에는 유저의 직접적인 정보를 갖고 있는 이메일이나 이름으로 저장 시, 외부에 노출 위험이 있기 때문에 유저의 PK 값으로 저장하였다.

 

# urls.py

urls.py 는 파일 명 그대로 경로 (엔드포인트)를 설정할 수 있다. urls.py도 models.py과 동일하게 변경 내역 없이 그대로 사용하였다.

from django.urls import path
from .views      import SignUp, SignIn

urlpatterns = [
    path('/signup', SignUp.as_view()),
    path('/signin', SignIn.as_view()),

 

# 결과

- 패스워드 암호화

회원 가입 요청 시 아래와 같이 DB에서 패스워드가 암호화가 되어있는 것을 확인할 수 있다.

3|hoon|$2b$12$w6OicdJx.ziWEvtUiQUzHuNFwqeQsh6UNwPK9gcyEnKaLqUu2OEs2|2020-02-14 12:47:22.144974|2020-02-14 12:47:22.150409
4|haha|$2b$12$1mdITD4bJ57S2Ub6KO8.quS5zM2I7SwfKclelglrB2x8x0OQo8J9q|2020-02-15 06:10:04.672130|2020-02-15 06:10:04.675798
6|abcd|$2b$12$VUIxEbEzfC5rkR.5sJTk6unXbDmCuuduhT8gDLIMozmMTgG1Fz0Ru|2020-02-15 12:04:38.956551|2020-02-15 12:04:38.958308

 

- 토큰 발행

DB에 저장되어있는 ID/PW로 로그인 시 아래와 같이 토큰이 발급되는 것을 확인할 수  있다.

wave@Waveui-MacBookPro http -v localhost:8080/account/signin email=abcd@naver.com password=abcd

POST /account/sign-in HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 36
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.0.0

{
    "name": "abcd@naver.com",
    "password": "abcd"
}

HTTP/1.1 200 OK
Content-Length: 114
Content-Type: application/json
Date: Sat, 15 Feb 2020 12:05:09 GMT
Server: WSGIServer/0.2 CPython/3.7.4
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWJjZCJ9.axd08-ceNjMB8koSRzMO1KTSQK3AaO45qJNpka1F74A"
}

 

* 관련 글

간단한 회원가입/로그인 예제
회원가입/로그인 + 암호화,토큰
회원가입/로그인 입력 값 필터
- 이메일 인증
- 유닛 테스트

Comments