티스토리 뷰

study/Django

Django - JWT 발급

xoxowo 2022. 7. 13. 22:45

JWT?

JWT(JSON Web Token)는 Json 포맷을 이용하여 Self-Contained 방식으로 사용자에 대한 정보를 저장하는 Claim 기반 Web 토큰으로 인가(Authorization)에 연관된 기술이다. 

 

 

클라이언트의 요청(링크 클릭 etc..)이 들어올 때마다 서버는 요청을 한 사용자가 로그인, 인증과정을 거친 상태인지 확인해서 블로그 게시글 작성, 메일 확인, 댓글 확인과 같은 로그인이 필요한 기능들에 대해 허용을 해야 할지 응답해야 하는데, 매번 로그인을 하기에는 로그인 작업이 무거운 작업이기 때문에 로그인에 성공했을 때 토큰이라는 것을 전달한다. 이후 클라이언트의 요청이 들어올 때마다 로그인 대신 토큰을 확인하여 로그인 기록을 확인하여 응답을 한다. (설명이 틀렸을 수 있음..🥹)

   

암호화 포스팅에서 회원 로그인 시 메시지에 해쉬 코드가 전달됐었는데, 이 부분이 암호화한 JWT이다.

HTTP/1.1 200 OK
Content-Length: 108
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Tue, 12 Jul 2022 10:06:09 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.8.13
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTJ9.
    RQynyR3xCDuT_lOQmD9Uc7aZse6CrEIespUXgpEh5rI"
}

 

구조

JWT의 구성요소는 헤더(Header)/ 페이로드(Payload)/서명 (Signature)세개의 파트로 나눠져 있으며 코드를 자세히 보면 점(.)으로 구분되어있다. 구성요소 중 서명(Signature) 부분이 가장 중요한 부분이다.

 

 

Header에는 토큰의 타입이나, 생성 시 어떤 알고리즘이 사용되어있는지 저장하고, Payload에는 Claim이라는 사용자에 대한 또는 토큰에 대한 정보가 들어가 있다.

 

가장 중요한 것은 이 Header와 Payload에는 중요 정보를 담으면 안 된다.  누구든지 이 jwt를 디코딩하면 Header와 Payload에 들어간 값을 알 수 있기 때문이다.  (위 사진과 같이 jwt.io에서 토큰에 대한 정보를 볼 수 있다.)

 

가장 중요한 Signature는 서버가 가지고 있는 개인키를 가지고 암호화가 되어있기 때문에 서버에 있는 개인키로만 암호화를 풀 수 있다.

즉 다른 클라이언트는 임의로 Signature를 복호화할 수 없다!

 

 

JWT 발급

JWT을 발급하는 과정을 정리하면, 

→ 클라이언트 사용자가 아이디, 패스워드를 가지고 로그인 인증

→ 로그인 성공 시 서버에서 서명된 JWT를 생성하여 클라이언트에게 응답

→ 인증된 클라이언트가 이후 데이터를 요청 시 받아둔 JWT를 HTTP header에 넣어 전달

→ 클 아이언 트로부터 온 JWT를 서버에서 검증

 

기존에 작성했던 views.py에 로그인 성공 시 응답에 JWT 토큰을 넣어 보내는 코드를 추가했다.

<전 포스팅에 작성한 코드는 피드백을 받고 더 정돈된 모습이 되었다.. 갈길이 멀다..🤣👍>

# users/views.py
import bcrypt 
  """ ...  생략 """

class SignInView(View):
    def post(self, request):
        try: 
            
            data = json.loads(request.body)
            
            email    = data['email']
            password = data['password']
            
            user = StoreUser.objects.get(email=email) 
            
	    # checkpw( 사용가 입력한 비밀번호 인코딩, 데이터테이블에 저장되어있는 디코딩된 비밀번호 다시 인코딩) 매치확인 
            if not bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
            	return JsonResponse({"message": "INVALID_USER"}, status = 401)       
   
            # 사용자 로그인 시 서버 시크릿키, 알고리즘으로 jwt 인코딩하여 토큰에 저장 후 response에 같이 전달
            access_token = jwt.encode({"id": user.id }, SECRET_KEY, ALGORITHM)
	    # jwt 중괄호에 들어있는 'id'도 user_id와 같이 가급적 외부에서 유추 가능한 정보를 담지않는게 좋다고 한다
            return JsonResponse({"message": access_token }, status = 200) 
            
        except StoreUser.DoesNotExist:
            return JsonResponse({"message": "INVALID_USER"}, status = 401)               

        except KeyError :
            return JsonResponse({"message": "KEY_ERROR"}, status = 400)

서버 로그인 성공 시 응답에 발급한 access_token이 메세지로 전송된 것을 확인할 수 있다

POST /users/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 48
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1

{
    "email": "u@naver.com",
    "password": "123qwe!!"
}

HTTP/1.1 200 OK
Content-Length: 108
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Tue, 12 Jul 2022 10:06:09 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.8.13
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTJ9.
    RQynyR3xCDuT_lOQmD9Uc7aZse6CrEIespUXgpEh5rI"
}

'study > Django' 카테고리의 다른 글

Django - 동일한 테이블을 참조하는 ForeignKey  (0) 2022.07.31
Django -path Variable  (0) 2022.07.24
Django - 암호화 (bcrypy)  (0) 2022.07.12
Django - 회원 로그인 구현  (0) 2022.07.10
Django - 회원가입 구현  (0) 2022.07.10
댓글