JWT란?
JWT(Json Web Token)는 클라이언트와 서버 간에 정보를 안전하게 전송하기 위한 개방형 표준입니다. 사용자 인증 정보를 포함하며, 해당 정보는 디지털 서명에 의해 검증될 수 있다.
JWT의 장점
-스테이트리스(Stateless): JWT는 요청이 독립적으로 서버에서 처리될 수 있도록 설계되었다. 즉, 서버는 클라이언트 상태를 기억할 필요가 없으며, 이로 인해 확장성이 뛰어나고 서버 부하를 줄일 수 있다..
-Self-contained: JWT는 필요한 모든 정보를 자체적으로 지니고 있다. JWT는 이 정보를 안전하게 전달하기 위해 디지털 서명된다.
-인증 및 교차 도메인 인증 정보 교환 용이: JWT는 사용자의 인증 정보를 가지고 있으므로, 웹사이트나 모바일 앱에서 로그인 세션을 유지하거나 API에서 사용자를 인증하는 등의 용도로 사용될 수 있다.
JWT의 단점
-크기: JWT는 많은 양의 메타 데이터를 포함하며, 세션 ID와 비교했을 때 상대적으로 크다. 따라서, 네트워크에서의 오버헤드를 증가시킬 수 있다.
-데이터 보안성: 페이로드(Payload) 정보는 기본적으로 암호화되지 않습니다. 따라서 중요한 개인 데이터가 포함되지 않도록 주의해야 한다. 또한, 토큰이 탈취되면 사용자의 정보가 노출될 수 있으므로 보안에 신경을 써야 한다.
-삭제가 어려움: JWT는 서버에서 발행되고 나면 만료되기 전까지는 클라이언트에서 계속 사용될 수 있다. 이는 로그아웃 또는 즉각적인 토큰 만료에 문제를 일으킬 수 있다.
authentication(인증) : 쉽게 말해 로그인, 내가 이 곳의 사용자임을 인증
authorization(인가) : 인증 받은 사용자가 내 계정으로만 할 수 있는 활동을 시도할 때, 로그인이 되고 나서 일어나는 일들
stateless : 이처럼 시간에 따라 바뀌는 어떤 상태 값을 안 갖는 걸 말함.
JWT를 이용한 로그인 시스템 구축 로직
- 사용자 인증: 사용자가 로그인 페이지에서 이메일, 비밀번호 등을 입력하고 '로그인' 버튼을 클릭하면, 이 정보는 서버로 전송된다. 서버는 이 정보를 데이터베이스와 비교하여 사용자를 인증한다.
- 토큰 생성: 사용자가 성공적으로 인증되면, 서버는 해당 사용자에 대한 JWT를 생성한다. 이 토큰에는 사용자 식별 정보(ID 등)가 포함되며, 이 정보는 비밀 키를 사용해 서명된다.
- 토큰 전송: 생성된 JWT는 사용자에게 응답으로 전송된다. 일반적으로 이 토큰은 HTTP 응답의 헤더 또는 바디에 포함되어 전송된다.
- 토큰 저장 및 사용: 클라이양트(브라우저나 앱)는 전달받은 JWT를 저장(일반적으로 로컬 스토리지나 쿠키)하고, 이후 서버에 요청할 때마다 이 토큰을 HTTP 요청 헤더에 포함시켜 인증 정보를 제공한다.
- 토큰 검증: 서버는 클라이언트의 요청을 받을 때마다 헤더에서 JWT를 추출하여, 서명을 확인함으로써 토큰의 유효성을 검증한다. 토큰이 유효하다면 요청을 처리하고, 그렇지 않다면 에러 메시지를 반환한다.
- 로그아웃 및 토큰 만료: 사용자가 로그아웃하면 클라이언트는 저장된 JWT를 제거한다. 또한, JWT에는 만료 시간(expiration)이 설정되어 있어 일정 시간이 지나면 자동으로 만료된다. 이 경우 사용자는 다시 로그인하여 새로운 토큰을 받아야 한다.
//사용자 로그인
const onSubmitHandlerForAdmin = async (data: IFormInput) => {
// Basic Authentication을 위한 header 설정
const auth = "Basic " + btoa(data.Username + ":" + data.Password);
await apiLogin
.post(
"/admins/login",
{},
{
headers: {
Authorization: auth,
},
}
)
.then((response) => {
localStorage.setItem("tokenForAdmin", response.data.accessToken);
localStorage.setItem("refreshToken", response.data.refreshToken);
setTokenForAdmin(response.data.accessToken);
alert("로그인 성공!");
navigate("/Home");
})
.catch((error) => {
alert("로그인 실패...");
});
};