jwt 발급과정

Seung jun Cha·2024년 7월 13일
0
  • 먼저 로그인 비즈니스 로직에서 사용되는 것 들이다. 아래에서 같이 설명한다.

  • 위에서부터 차례대로 설명한다.

  • 먼저 authenticateUser 메서드이다. 반환되는 Authentication 객체는 Spring Security의 인증 과정을 통해 생성되며 아이디와 비밀번호를 입력해서 로그인에 성공한 사용자의 인증정보를 담고 있다. getName, getAuthorities, getPrincipal 등의 메서드로 사용자정보를 가져올 수 있다.

  • 엑세스토큰과 리프레쉬토큰을 생성하는 메서드이다. 엑세스토큰을 생성할 때는 인증된 사용자정보가 사용된다. 리프레시 토큰을 생성할 때는 Authentication 객체가 필요하지 않은 이유는 리프레쉬토큰은 단순히 액세스 토큰을 갱신하는 데에만 사용되기 때문이다.

  • 토큰을 생성하는 코드가 있는 JwtTokenProvider로 가보자. 여기서 toString 메서드는 사용자가 가진 권한을 나타내는 GrantedAuthority 객체들의 컬렉션을 문자열로 변환하는 메서드이다.
  • getAuthority() 메서드를 통해 해당 권한들의 문자열을 가져오고 Collectors.joining(AUTHORITIES_DELIMITER)를 사용하여 각 권한 문자열을 구분자(AUTHORITIES_DELIMITER)로 연결하여 하나의 문자열로 만든다.
  • 사용자 정보는 토큰의 페이로드에 들어가는데 간결하게 표현하고 좀 더 쉽게 다루기위해 하나의 문자열로 바꾼다

  • JWT 페이로드의 예시이다. 페이로드는 Base64 URL-safe 인코딩으로 인코딩되어 있으며, 디코딩하면 JSON 형태의 데이터를 확인할 수 있다.
디코딩 전
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMSIsInJvbGVzIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjI2MTQ5OTk5fQ.rfr3r5kU9sHuyu9cyEe5EBvGosRFF4s0PfPdS91QDSI

디코딩 후,

{
  "sub": "user1",
  "roles": "ROLE_USER",
  "exp": 1626149999
}
  • 엑세스토큰과 리프레쉬토큰 생성 메서드이다. 토큰 설정정보가 있는 JwtProperties 클래스로 이동해보자.

  • @Value로 설정된 값을 가져와서 세팅해준다.
  1. secretKey: JWT를 서명하기 위한 비밀 키로 서명된 토큰을 검증할 때 사용된다. 인코딩된 페이로드를 디코딩할 때 사용된다.

  2. accessTokenValidTime: 발급된 액세스 토큰의 유효 시간으로 토큰은 발행 시점에서 유효 시간 만큼만 유효하며, 이 시간이 지나면 만료된다.

  3. refreshTokenValidTime: 발급된 액세스 토큰의 유효 시간으로 토큰은 발행 시점에서 유효 시간 만큼만 유효하며, 이 시간이 지나면 만료된다. 리프레시 토큰은 주로 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용된다.

  4. issuer: JWT의 발급자(issuer) 정보이다. 발급된 토큰이 어디에서 발급되었는지를 나타내는 식별자이다.

  5. tokenPrefix: 클라이언트가 JWT를 식별할 수 있도록 토큰 앞에 붙이는 접두어입니다. 예를 들어, "Bearer "와 같이 인증 헤더에 토큰을 식별하기 위해 사용된다.

  6. header: HTTP 요청에서 JWT를 식별할 헤더의 이름입니다. 보통 "Authorization" 헤더에 JWT를 실어 보낸다.

  • 생성된 리프레쉬토큰을 저장하는 로직이다. refreshTokenCommand와 RefreshToken 클래스로 가보자.

  • 그럼 이제 RefreshToken 클래스로 가보자. RefreshToken를 redis에 저장하기 위한 코드이다.

  • @RedisHash: 이 클래스가 Redis의 해시(hash) 데이터 구조로 저장될 것임을 나타내는 애노테이션입니다. 여기서 value는 Redis에서 해당 클래스의 객체가 저장될 해시의 이름을 의미한다. 뒤에 TimeToLive는 레디스에 저장된 값의 저장기간을 의미한다.

  • @Id : redis의 key로 사용하겠다는 의미이다. 여기서는 redis에 값이 저장될 때 userId가 key, 저장되는 값이 value가 되는 것이다.

  • 결과를 보면 "refreshToken:{userId}" 형식으로 key가 형성되고 value로 refreshToken의 값이 들어갔다.

  • 앞에 있는 refreshToken은 아까 말한대로 @RedisHash로 생성된 해시의 이름이다.

  • timeToLive와 jwt토큰의 expiration의 차이점

    • redis에서 해당 객체의 데이터가 유효한 시간을 지정하고, TTL이 지나면 Redis에서 자동으로 삭제된다
    • JWT 토큰의 만료 시간을 설정하여, 특정 시점 이후에 토큰이 더 이상 유효하지 않음을 의미한다.
    • Redis에서 객체의 TTL을 따로 설정하지 않으며, 객체는 수동으로 삭제하거나 다른 TTL 설정 방법을 사용해야한다.

  • addTokenCookie 메서드를 통해 특정 타입의 토큰을 HTTP 응답에 쿠키로 추가하는 기능을 구현한다.
    • HttpServletResponse response : HTTP 응답 객체에 쿠키를 담기 위함
    • TokenType tokenType : 토큰의 타입
    • String tokenValue : 토큰 값
    • long tokenLifeTime : 토큰의 유효기간
  • 좀 더 자세히 보기위해 CookieUtil로 이동한다. 쿠키에서 제공하는 메서드로 쿠키 옵션을 설정하고 쿠키를 생성하는 메서드이다.
  1. HttpServletResponse response: HTTP 응답 객체
  2. String name: 쿠키의 이름
  3. String value: 쿠키의 값
  4. long maxAge: 쿠키의 생명 주기
  5. String domain: 도메인 설정
  6. String sameSite: SameSite 설정

  • ResponseCookie.from(name, value) : 주어진 이름(name)과 값(value)으로 ResponseCookie 객체를 생성
  • maxAge() : 쿠키의 생명 주기 (TTL)를 설정
  • path("/") : 쿠키의 경로를 설정합니다. 여기서는 모든 경로에서 쿠키가 유효하도록 /로 설정
  • domain(domain) : 쿠키가 유효한 도메인을 설정
  • httpOnly(true) : 쿠키를 HTTP 전용으로 설정하여, 클라이언트 측 스크립트에서 접근할 수 없도록 함
  • secure(true) : 이 속성이 true로 설정되면, 쿠키는 HTTPS 연결에서만 전송
  • sameSite() : 쿠키의 SameSite 속성을 설정하여, CSRF 공격을 방지
  • response.addHeader("Set-Cookie", cookie.toString()) : 빌드한 ResponseCookie 객체를 문자열로 변환하여 HTTP 응답 헤더에 추가

마지막으로 아래 코드는 생성된 엑세스 토큰을 HTTP 응답 헤더에 세팅하는 코드이다. "Authorization"는 설정하려는 헤더의 이름으로 인증 토큰을 담는 데 사용된다.
response.setHeader("Authorization", accessTokenValue);

0개의 댓글