JWT를 이용한 인증 로직(Nest JS 예제)

JWT란?

jwt()는 header.payload.signature 3 부분으로 구성됨.

  • Header

    토큰의 타입과 해싱 알고리즘 정보를 포함합니다.

  • Payload

    사용자 정보나 기타 데이터를 포함합니다. 이 부분은 Base64Url로 인코딩되지만, 암호화되지는 않습니다.

  • Signature

    Header와 Payload를 합친 후 비밀 키를 사용하여 해싱한 값입니다.

JWT 특징

그럼 대체 왜 JWT를 사용할까?

장점

1. 서명된 토큰

JWT는 서명되어 있어 토큰의 무결성을 보장합니다. 토큰이 생성된 이후에 내용이 변경되지 않았음을 검증할 수 있습니다. 서명은 비밀 키를 사용해 생성되므로, 서명 키를 모르는 사람은 유효한 토큰을 생성할 수 없습니다.

또한 중간에서 토큰의 내용이 변경되면 서명이 유효하지 않게 되므로 토큰의 무결성도 보장할 수 있습니다.

2. Stateless(무상태)와 탈중앙화

JWT는 자체 포함된(self-contained) 토큰입니다. 토큰 자체에 사용자 정보와 같은 페이로드를 포함하고 있어, 토큰을 검증하는 동안 별도의 데이터베이스 조회가 필요하지 않습니다. 예전에는 인증을 위해서 세션을 많이 사용했습니다. 세션이란 서버에 저장된 유저들의 접속 정보라고 생각하면 됩니다. 이런 세션을 활용하기 위해서 계속해서 세션 정보가 있는지 체크했습니다. JWT는 토큰 자체에 사용자 정보가 있기 때문에 데이터베이스를 탐색할 필요가 없습니다.

이런 이유로 탈중앙화도 가능합니다. 세션을 사용할 때는 세션 정보가 각 서버에 따로 저장되어있는 상태라면 서버마다 가지고 있는 세션 데이터가 달라져 각 서버마다 인증할 수 있는 정도가 달라졌습니다. 물론 이 경우에도 여러 서버가 동시에 접속할 수 있는 데이터베이스에 세션 정보를 저장하면 문제를 해결할 수 있긴 합니다. 하지만 JWT는 더 쉽게 탈중앙화가 가능합니다. JWT 그 자체에 사용자 정보가 존재하기 때문에 어떤 서버에서 JWT 토큰을 받던 같은 정보를 얻을 수 있기 때문입니다.

단점

1. 탈취 시 해결 힘듬

JWT 페이로드는 인코딩만 되었을 뿐 암호화된 것이 아닙니다. 이런 이유로 JWT 토큰 자체가 탈취당한다면 누구나 JWT 토큰을 열어 페이로드를 확인할 수 있습니다. 또한 해당 토큰을 이용할 수도 있습니다. JWT 토큰을 뺏기면 모든 권한을 뺏기는 것과 같습니다.

이를 해결하기 위해 토큰 만료 기한을 짧게 정해서 바로바로 만료되도록 구성하기도 합니다.

2. 크기

페이로드와 서명을 포함하고 있으므로 JWT 토큰이 커질 수 있습니다.

Nest JS에서 JWT 실습하기

JWT 토큰 인코딩

# auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

위와 같은 코드를 통해 JSON 데이터에 access_token이라는 key값의 value에 JWT 토큰을 넣어 반환할 수 있습니다.

JWT 토큰 디코딩

# auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

   async decodeToken(token: string) {
    try {
      const decoded = this.jwtService.verify(token);
      return decoded;
    } catch (error) {
      throw new Error('Invalid token');
    }
  }
}

위 코드를 통해 JWT 토큰을 디코딩할 수 있습니다. 만약 해당 토큰이 임의로 사용자가 만든 토큰이라면 서명키값이 다르기 때문에 유효하지 않은 토큰으로 판단되어 에러를 반환합니다.

results matching ""

    No results matching ""