본문 바로가기
마루아라는 개발쟁이

HTTP 인증방식2_토큰기반 인증, JWT

by 마루아라 이야기 2023. 1. 2.
반응형

저번 포스팅에서 HTTP에서 인증이 필요한 이유와 인증방식 중 하나인 세션/쿠키기반 인증방식에 대해서 알아보았습니다. 세션/쿠키는 별도의 세션저장소를 두기때문에 DB의 부하가 생길수 있으며 확장성의 문제, 세션 하이재킹의 위험이 있었습니다. 이를 보완해서 나온 인증방식이 바로 토큰기반 인증방식입니다. 이번 포스팅에서는 토큰기반 인증을 알아보고 토큰기반 인증 중 가장 많이 사용되는 JWT에 대해서 알아보겠습니다.


🌱 토큰 기반 인증 동작방식

토큰 기반 시스템의 구현 방식은 시스템마다 크고작은 차이가 있겠지만, 대략적으로 보면 다음과 같습니다.

1. 사용자 로그인 요청

2. 서버에서 회원인지 확인합니다.

3. 계정정보가 정확하다면 유저에게 Access 토큰(Signed Token이라고도 합니다) 발급합니다.

 : 토큰에는 유효기간을 설정해야하며, 인증에 필요한 정보를 암호화하여 토큰을 발급합니다. 

4. 로그인 요청에 대한 응답과 함께 토큰을 클라이언트에게 보냅니다.

5. 클라이언트 측에서는 토큰을 저장해두고 서버에게 요청할 때 마다 함께 보냅니다.

6. 서버는 해당 토큰을 복호화한후 유효한 토큰인지 검증합니다.

7. 검증에 성공하면, 해당 사용자에 맞는 데이터를 응답합니다.

 

🌱 토큰 기반 인증 장단점

[ 장점 ]

1. Stateless 서버

: 세선/쿠키 인증방식은 서버에서 별도의 세션 저장소에 클라이언트 상태를 계속 유지하고 이 정보를 서비스에 이용하는 Stateful 서버였습니다. 하지만 토큰에는 인증에 필요한 모든 정보를 담고 있으므로, 서버에 별도의 세션저장소가 필요하지 않으며 서버는 클라이언트측에서 들어오는 요청으로만 작업을 처리합니다. 

2. 높은 서버 확장성

 : 토큰 기반 인증은 Stateless 서버, 즉 클라이언트와 서버와의 연결고리가 없기때문에 서버의 확장성이 좋아집니다. 여기서 말하는 서버의 확장성이란 많은 트래픽을 감당하기 위하여 여러개의 프로세스를 돌리거나 여러대의 서버 컴퓨터를 추가 하는것을 의미합니다.

3. 확장성(Extensibility)

: 여기서의 확장성은 로그인 분야의 확장되는 것을 의미합니다. 토큰을 사용하여 다른 서비스에서도 권한을 공유할 수 있습니다. 대표적인 예제로 OAuth가 있습니다. 이를 통해 페이스북/구글/네이버 계정을 이용하여 다른 웹서비스에 로그인할 수 있습니다.

 

[ 단점 ]

1. 이미 발급된 토큰에대해서는 돌이킬 수가없습니다. 

   : 세션/쿠키 방식에서는 서버가 클라이언트의 세션을 삭제시켜 강제 로그아웃이 가능했지만, 토큰은 한번 발급되게되면 유효기간이 지날때까지 계속 사용이 가능합니다. 

   -> 서버에서 토큰을 제어할 수 없습니다!

   -> Access Token의 유효기간을 짧게하고 Refresh Token이라는 새로운 토큰을 발급하면 해결할 수 있습니다. 

 


🌱 JWT(JSON Web Token)?

   토큰 기반 인증 중 가장 많이 사용되는 것이 JWT(JSON Web Token)입니다. JWT는 (인증)정보를 담고있는 JSON 객체를 암호화시키고 HTTP 헤더에 추가하여 (인증)정보를 안전하게 전송합니다. 이때 암호화는 비밀키(HMAC)또는 공개키/개인키쌍(RSA)을 이용합니다.

   또한 JWT는 개방형 표준RFC 7519 )입니다. 따라서 Java, Ruby, PHP, Python 등 여러 환경에서 지원이 가능합니다. 구글, 마이크로소프트와 같은 수많은 회사에서 이를 사용하고 있습니다.

 

🌱 JWT의 구조

  JWT는 Header, Payload, Signature로 이루어져있습니다.

Header에는 토큰의 타입이 JWT라는 것과 무슨 해싱알고리즘(HMAC/RSA)을 사용하였는지 정보가 담겨있습니다.

Payload에는 사용자의 인증정보가 담겨져있습니다.

Signature에는 전자서명이 들어가게됩니다. 이 서명은 헤더에서 명시한 해싱을 통해 생성됩니다.

 

이에 대한 더 자세한 내용은 JWT 공식 사이트에서 확인하실 수 있습니다.

 

🌱 JWT 실제 사용 코드

: 실제 제가 JWT를 사용해서 만든 Flask 프로젝트를 보면 다음과 같습니다.

[ 토큰 발급 ]

# 로그인 API
@app.route('/api/login', methods=['POST'])
def api_login():
    id_receive = request.form['id_give']
    pw_receive = request.form['pw_give']
    # 비밀번호 암호화하여 저장
    pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()
    # 회원정보 확인
    result = db.user.find_one({'id': id_receive, 'pw': pw_hash})
    
    # 토큰 발급
    if result is not None:
        payload = {
            'id': id_receive,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

        return jsonify({'result': 'success', 'token': token})

    else:
        return jsonify({'result': 'fail', 'msg': '아이디와 비밀번호를 입력해주세요'})

 

 

토큰 발급 라인부터 주의깊게 보면된다.

JWT의 Payload에는 인증에 필요한 id와 토큰 유효기간을 exp를 지정해주었다.

이후 Hash256을 사용하여 암호화하였다.

[ 토큰 검증 ]

# 채널 삭제
@app.route('/api/channel/delete', methods=['POST'])
def channel_delete():
    channel_id = request.form['channel_id']
    # 토큰 가져오기
    token_receive = request.cookies.get('mytoken')
    try:
    	# 토큰 복호화
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        # payload에서 id정보 꺼내서 사용
        db.channel.delete_one({'_id': ObjectId(channel_id), 'user_id': payload["id"]})
    except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
        return redirect(url_for("home"))

    return jsonify({'msg': '삭제되었습니다!'})

: 쿠키에서 토큰을 꺼내온 후에 복호화를 진행하였다.

복호화한 토큰에서 payload의 유저 인증 정보(id)를 꺼내와서 인증에 사용하였다.

 

[ 브라우저 ]

브라우저 쿠키에 있는 mytoken 확인

 

 

출처:

JWT 공식 사이트

728x90
반응형
LIST