2024.05.08(수) 슈퍼코딩 부트캠프 신입연수원 WEEK 2 Day 3 일일보고

usnim·2024년 5월 8일
0

super_coding

목록 보기
8/35

📋 TO-DO LIST

신입 연수원 활동 [o]

2주 3일차 강의 [o]

인턴교육 직후 사수 도움 요청 업무 [o]

팀장님 지시 업무 [o]

중간/일일 업무 보고 작성 [o]

정기 팀/동기 스터디 모임 참석 및 성실도 [o]

📍 학습 내용 정리

JWT(Json Web Token)?

JWT는 유저를 인증하고 식별하기 위한 토큰(Token) 기반 인증입니다.

토큰 자체에 사용자의 권한 정보나 서비스를 사용하기 위한 정보가 포함됩니다.

RESTful과 같은 무상태(Stateless)인 환경에서
사용자 데이터를 주고받을 수 있게 됩니다.

세션(Session)을 사용하게 될 경우 쿠키 등을 통해 사용자를 식별하고 서버에 세션을 저장했지만, 토큰을 클라이언트에 저장하고 요청시 HTTP 헤더에 토큰을 첨부하는 것만으로도 단순하게 데이터를 요청하고 응답받을 수 있습니다.

JWT(Json Web Token) 구조

출처 : https://velopert.com/2389

JWT는 세 파트로 나뉘며 , 각 파트는 .(점)으로 구분됩니다.

header

서명 시 사용하는 키(kid), 사용할 타입(typ), 서명 암호화 알고리즘(alg)의 정보가 담겨 있습니다.

{
 "kid" : "Key ID",
 "typ" : "JWT", 
 "alg" : "ES256"
}
kid : 서명 시 사용하는 키(Public/Private Key)를 식별하는 값

typ : 토큰 유형

alg : 서명 암호화 알고리즘 

HS256(HMAC SHA-256),
HS512, RS256(RSASSA SHA-256),
ES256(ECDSA P-256 curve SHA-256)

payload

토큰에서 사용할 정보의 조각들인 클레임(Claim)이 담겨 있습니다.

클레임(Claim)은 Key/Value 형태로 된 값을 가집니다.

저장되는 정보에 따라 등록된 클레임(Registered Claims),
공개 클레임(Public Claims), 비공개 클레임(Private Cliams)로 구분됩니다.

{
  "iss" : "growthplatform",
  "sub" : "example",
  "iat" : "1802189795,
  "exp" : "1802193395,
  "roles" : "ROLE_SUPER"
}
iss : 토큰 발급자(issuer) – Public Claims

sub : 토큰 제목(subject) – Public Claims

iat : 토큰 발급 시간(issued at) – Public Claims

exp : 토큰 만료 시간(expiration) – Public Claims

roles : 권한 – Private Cliams

signature

Header(헤더) 에서 정의한 알고리즘 방식(alg)을 활용합니다.

Header(헤더)+ 페이로드(Payload)와 서버가 갖고 있는
유일한 key 값을 합친 것을 헤더에서 정의한 알고리즘으로 암호화합니다.

ECDSASHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),

Public Key in SPKI, PKCS #1,
X.509 Certificate, or JWK string format.

Private Key in PKCS #8, PKCS #1,
or JWK string format. The key never leaves your brower.

Header(헤더) 와 페이로드(Payload)는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature(서명)는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없습니다. 이는 토큰의 위변조 여부를 확인하는데 사용됩니다.

JWT를 이용한 회원가입 & 로그인

회원가입

회원 가입 과정에서 사용자는 웹사이트에
자신의 정보(이름, 이메일, 비밀번호 등)를 제공합니다.

이 정보는 데이터베이스에 저장되며, 비밀번호는 보안을 위해 해시화하여 저장합니다.

로그인

로그인 과정에서 사용자는 이메일과 비밀번호를 입력하여 서버에 전송합니다.

서버는 데이터베이스에서 해당 이메일을 찾아 입력된 비밀번호와 저장된 비밀번호 해시가 일치하는지 확인합니다.

비밀번호가 일치하면, 서버는 JWT를 생성합니다.

이 토큰은 사용자의 고유 정보 (예: 사용자 ID)와 서버의 비밀키를 이용하여 만들어지며, 이렇게 만들어진 JWT는 사용자에게 전송됩니다.

AccessToken & RefreshToken

Access Token : 사용자 인증만을 위한 토큰(비교적 짧은 만료시간)

Refresh Token : Access Token 만료시
새로운 Access Token 발급을 위한 Token(비교적 긴 만료시간)

※ JWT는 탈취 위험성이 높기 때문에 그대로 사용하지 않고 Access Token과 Refresh Token으로 나누어 인증한다.

  • 일반적으로 로그인 같은 과정을 하면 Access Token과 Refresh Token을 모두 발급한다.

  • Refresh Token만 서버측의 DB에 저장, Refresh Token과 Access Token을 쿠키 혹은 웹스토리지에 저장

  • 사용자가 인증이 필요한 API에 접근하고자 하면, 가장 먼저 토큰을 검사

  • 이때, 토큰을 검사함과 동시에 각 경우에 대해서 토큰의 유효기간을 확인하여 재발급 여부를 결정한다.

  • Access token과 Refresh token 모두가 만료된 경우 → 에러 발생 (재 로그인하여 둘다 새로 발급)

  • Access token은 만료됐지만, Refresh token은 유효한 경우 → Refresh token을 검증하여 Access token 재발급

  • Access token은 유효하지만, Refresh token은 만료된 경우 → Access token을 검증하여 Refresh token 재발급

  • Access token과 Refresh token 모두가 유효한 경우 → 정상 처리

  • 로그아웃을 하면 Access Token과 Refresh Token을 모두 만료시킨다.

https://velog.velcdn.com/images/moduri/post/c1aada84-5bdf-47ab-b607-990da0c5134d/image.png

LocalStorage & SessionStorage

SessionStorage

  • 세션 스토리지는 새창, 새탭의 단위로 스토리지가 생성되며
    데이터를 공유하지 않습니다.

  • 또한 같은 탭이라도 도메인이 다르면 또다른 세션 스토리지가 생성 됩니다.

  • 한마디로 세션스토리지는 도메인, 윈도우 탭별 독립적인 공간입니다.

  • 그렇기때문에 사용자가 브라우져의 창을 닫거나 탭을 닫으면 저장된 객체가 사라져 잠깐의 정보를 저장하기에 용이 합니다.

  • 주로 회원가입 입력폼, 일회성 로그인에 사용됩니다.

LocalStorage

  • 로컬 스토리지는 웹 도메인당 1개씩 생성되며 세션 스토리지와 다르게 새창을 띄워도 도메인만 같으면 동일한 데이터를 공유하게 되고

  • 다른 도메인의 로컬 스토리지에는 접근이 불가능 합니다.

  • 또한 직접 로컬 스토리지를 삭제하지 않으면 영구적으로 데이터를 저장합니다.

  • 쿠키를 이용하지 않는 앱 환경에서 자동 로그인등에 주로 사용됩니다.

📍 일일보고

부족한 점 : 비동기함수가 적용가능한 것인지 한번 더 체크하는 습관 부족

스스로 시도해본 것들 : 회원가입 api 작성시 비밀번호 해쉬화 & 로그인 후 jwt accessToken 발급 (api)

from fastapi import FastAPI , Query , HTTPException
from pydantic import BaseModel
from typing import List
from fastapi.staticfiles import StaticFiles
from pymongo import MongoClient
from bson import ObjectId 
import json
import hashlib
import jwt
import datetime

async def hash_password(password):
  return hashlib.sha256(password.encode()).hexdigest()

async def generateJWT(email , user_id):
  payload = {
    "email" : email,
    "user_id" : user_id,
    "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
  }
  token = jwt.encode(payload,secret_key,algorithm="HS256")
  print("token",token)
  return token

@app.post('/signup')
async def add_user(email:str,password:str):
  hashed_password = await hash_password(password)
  print(hashed_password)
  data = {
    "email": email,
    "password": hashed_password
  }
  result = collection.insert_one(data)
  return {"message": "유저가 추가되었습니다.", "user_id": str(result.inserted_id)}

# post method
@app.post('/login')
async def login_user(email:str,password:str):
  user = collection.find_one({"email":email})
  if user is None or user["password"] != await hash_password(password):
        raise HTTPException(status_code=401, detail="인증 실패: 이메일 또는 비밀번호가 올바르지 않습니다.")
  user_id = str(user["_id"])  # 사용자 ID를 문자열로 변환
  token = await generateJWT(email, user_id)
  
  return {"user_id": user_id, "token": token}

해결 내용 : 비동기처리 불가능 에러 확인 후 적절한 await구문 추가 수정 및 삭제 진행

알게된 점 : 로그인 진행시 회원가입 했던 비밀번호는 복호화된 비밀번호가 DB에 삽입되어있기 때문에 비교를 위해 로그인시 비밀번호도 복호화를 진행 후 비교검증을 해야하는걸 인지

헷갈리거나 실수한 점 : JWT encode시 payload의 데이터를 전역으로 설정해야하나 고민이 되었지만 함수로 설정 후 generateJWT안에서 payload를 선언 후 활용

회고 : 2주 3일차를 진행하면서 새로운 라이브러리들을 활용하고 api의 필수라고 생각되는 회원가입과 로그인 api를 작성해보고 파생되는 다른 api들도 작성해보고 싶다

profile
🤦‍♂️

0개의 댓글